WordPress at Your Fingertips
rgbcode is looking for WordPress developers.

WP_Query{}WP 1.5.0AllowDynamicProperties

The WordPress Query class. Selects posts from the database based on the specified parameters. get_posts(), query_posts() and all other query functions for posts selection from wp_posts table are based on this class.

When creating queries, WP_Query may receive pre-set parameters that need to be taken into account.

For example, the query $query = new WP_Query( 'post_type=post' ); may return only the first 10 "post" type entries, instead of all as expected. This is because the pre-set parameter posts_per_page is taken from the option get_option('posts_per_page').

List of parameters that can be pre-set and may interfere with your query:

post_type
posts_per_page
orderby
order
post_status
offset

Ticket #18408 WP_Query behaves abnormally in the Admin Panel, particularly wp_reset_postdata() may not behave as expected. It's better to use get_posts() in the admin area.

Return

WP_Query object. Object property ->posts contains array of posts (WP_Post objects).

Introduction to WP_Query

WP_Query is a PHP class that allows you to retrieve posts from the database based on various criteria. For example, you can retrieve posts:

  • within a specific time frame;
  • from a specified category, tags;
  • fresh posts, random posts, popular posts;
  • posts with specified custom fields or a set of such fields.

WP_Query is used in WordPress for any post-related queries. For example, during the generation of any WP page, the WP_Query request is stored in the global variable $wp_query, and besides post data, it also contains information about the current page. No generation of a basic WP page is complete without using WP_Query.

When building the standard WordPress loop, data is taken from the global variable $wp_query and displayed on the screen through auxiliary functions: the_permalink(), the_content().

In addition to the list of posts in $wp_query, information about the current request is stored. Based on this information, WP determines which page we are currently on (post, archive, tag, etc.). Information about the current request is only relevant for the global object $wp_query (for objects in subqueries, this information is meaningless). You can view what is in the global object like this:

print_r( $GLOBALS['wp_query'] );

Often, we obtain information without directly working with this class and global variables – all of this is hidden under the hood. We use a set of ready-made functions: conditional tags, template tags related to outputting data within the Loop, and other functions. Almost all conditional tags refer to this class.

Let's look at a simple query:

$query = new WP_Query( [ 'category_name' => 'news' ] );

In this way, WordPress will make a request to the database to retrieve entries from the "news" category.

Now, the variable $query contains an object with the query results. Let's process the result using special methods:

while ( $query->have_posts() ) {
	$query->the_post();

	the_title(); // output the post title
}

wp_reset_postdata(); // IMPORTANT to restore global $post

If the_post() is used in the loop, then it is mandatory to call the function wp_reset_postdata() after the loop.

have_posts() checks if there are posts in the loop (in the $query object). the_post() sets global $post – the next post from the loop is recorded there, and it also sets other global variables – see setup_postdata(). All of this allows using familiar WP functions in the loop without passing parameters: the_title(), the_content(), etc.

For more details on how this works, read about have_posts() and the_post().

How not affect global $post

If we don't need standard functions that work with global $post, we can use $query->next_post() instead of $query->the_post(). In this case, the global variable $post is not affected:

while ( $query->have_posts() ) {
	$my_post = $query->next_post();

	$url = get_permalink( $my_post );
}

WP_Query via foreach

If no parameters are passed, no request will be made, and just a WP_Query object will be created.

The loop code may look like this (this is the same as get_posts(), but without pre-set parameters):

// create an instance
$my_posts = new WP_Query;

// make a request
$myposts = $my_posts->query( [
	'post_type' => 'page'
] );

// process the result
foreach( $myposts as $pst ){
	echo esc_html( $pst->post_title );
}

For a better understanding and visual perception of the query functions, study the image:

Credit: Andrey Savchenko (rarst.net) / CC-By-SA.

Usage Examples

0

#1 Standard Loop

// set the necessary criteria for selecting data from the database
$args = array(
	'posts_per_page' => 5,
	'orderby'        => 'comment_count'
);

$query = new WP_Query( $args );

// loop
if ( $query->have_posts() ) {
	echo '<ul>';
	while ( $query->have_posts() ) {
		$query->the_post();
		echo '<li>' . esc_html( get_the_title() ) . '</li>';
	}
	echo '</ul>';
} else {
	// no posts found
}
// Return back the original post data. Reset $post.
wp_reset_postdata();
0

#2 Multiple Loops

// query to DB
$the_query = new WP_Query( $args );

// loop
while ( $the_query->have_posts() ) {
	$the_query->the_post();
	echo '<li>' . get_the_title() . '</li>';
}

/*
Restore the original data.
We use new WP_Query, this means we have no impact on the global
variable '$wp_query', so there is no need to reset the query and
only post data can be reset.
*/
wp_reset_postdata();

// Second query (without global variable)
$query2 = new WP_Query( $args2 );

// 2nd loop
while( $query2->have_posts() ) {
	$query2->next_post();
	echo '<li>' . get_the_title( $query2->post->ID ) . '</li>';
}

// Restore original post data
wp_reset_postdata();
0

#3 get_posts() analogue

$args = [
	'post_type' => 'page'
];
$query = new WP_Query;
$my_posts = $query->query( $args );

foreach( $my_posts as $my_post ){
	echo '<p>'. $my_post->post_title .'</p>';
}

See below the list of all possible parameters or in the code of WP_Query::parse_query() method.

-1

#4 Standard Loop (Alternate)

<?php

// the query
$the_query = new WP_Query( $args ); ?>

<?php if ( $the_query->have_posts() ) : ?>

	<!-- pagination here -->

	<!-- the loop -->
	<?php while ( $the_query->have_posts() ) : $the_query->the_post(); ?>
		<h2><?php the_title(); ?></h2>
	<?php endwhile; ?>
	<!-- end of the loop -->

	<!-- pagination here -->

	<?php wp_reset_postdata(); ?>

<?php else : ?>
	<p><?php esc_html_e( 'Sorry, no posts matched your criteria.' ); ?></p>
<?php endif; ?>

WP_Query Parameters

Category

Gets posts from specified categories.

cat(int/string/array)
The ID of the category. You can specify multiple IDs in a comma-separated string or in an array. To exclude category, specify the minus sign (-) before the ID of the category (it also exclude all sub-categories). To skip excluding sub-categories, use the category__not_in.
category_name(string)
Category name. Use a slug (alternative name) rather than the category name itself.
category__and(array)
Get posts that fall into multiple categories at the same time.
category__in(array)
Get posts that fall into one of these categories.
category__not_in(array)

Get posts that are not in the specified categories.

This parameter does not consider the nested categories. To exclude a category and nested categories, specify the category ID separated by a comma with a minus sign in cat parameter: cat=-25,-32.

Posts from one category

Get posts from category with id 4 (posts from child categories will also be selected):

$query = new WP_Query('cat=4');

Get posts from one category by a slug (alternative name of the category):

$query = new WP_Query('category_name=staff');

Get posts only from category 4 (child categories are not affected):

$query = new WP_Query( 'category__in=4' );

Posts from several categories

Output posts from categories by categories IDs:

$query = new WP_Query('cat=2,6,17,38');

Get posts from categories by their slug:

$query = new WP_Query( 'category_name=staff,news' );

Exclude posts belonging to categories

Let's output all posts, except the ones with categories 12,34,56:

$query = new WP_Query( 'cat=-12,-34,-56' );

Show all posts except those that belong to category 3:

$query = new WP_Query('cat=-3');

Posts from multiple categories simultaneously

Show posts that are in two categories at once:

$query = new WP_Query( array( 'category__and' => array(2,6) ) );

Show posts that are in at least one of the categories (child categories will not be considered):

$query = new WP_Query(array('category__in' => array(2,6)));

You can exclude several categories, like this:

$query = new WP_Query( array('category__not_in' => array(2,6)) );

Tags

Gets posts related to specific tags.

tag(string)
Tag slug.
tag_id(int)
Tag ID.
tag__and(array)
Posts with several tags. You must specify ID.
tag__in(array)
Posts with at least one of the specified tags. You must specify ID.
tag__not_in(array)
Posts not related to the specified tags. You must specify ID.
tag_slug__and(array)
Same as tag__and, but tag slug (alternative name) should be specified (instead of ID).
tag_slug__in(array)
Same as tag__in, but tag slug (alternative name) should be specified (instead of ID).

Posts with one tag

Get posts with the tag cooking. You must specify the tag slug, but not ID.

$query = new WP_Query('tag=cooking');

Get posts that have at least one specified tag:

$query = new WP_Query('tag=bread,baking');

Posts with multiple tags

Get posts that have one of these tags:

$query = new WP_Query( 'tag=bread,baking' );

Get posts having all specified tags at once:

$query = new WP_Query('tag=bread+baking+recipe');

Get posts with two tags (37 and 47). This option is more preferable to the above because of the performance.

$query = new WP_Query( array('tag__and' => array(37,47)) );

Get posts with at least one of these tags (37 or 47). This option is more preferable because you specify IDs (so code works more quickly).

$query = new WP_Query( array('tag__in' => array(37,47)) );

Get posts that don't have tags with ID 37 and 47:

$query = new WP_Query(array( 'tag__not_in'=>array(37,47) ));

Taxonomy

Gets posts related to a particular taxonomy.

{tax}(string)
Use taxonomy name and term slug: array('category'=>'wordpress'). Deprecated since version 3.1 in favor of 'tax_query'.
tax_query(array)

Since versions 3.1. You must specify the query parameters for the taxonomy. It can take a number of nested arguments, such as:

  • relation(string)
    How to select posts from specified taxonomies. Can be:

    • AND - posts that are simultaneously included in the specified taxonomies terms.
    • OR - posts belonging to any of the specified taxonomies terms.

    It is specified in the first array, and the terms queries specified in nested arrays, which can have the following parameters:

    • taxonomy(string)
      The name of the taxonomy.

    • field(string)
      The field that will be specified in the terms parameter. Can be:

      • id or term_id - specify term id in terms parameter.
      • name - specify term title (name) in terms parameter.
      • slug - specify term slug (alternative name) in terms parameter.

        Default: 'term_id'

    • terms(int/string/array)
      The terms of the taxonomy from which you want to get the posts.

    • operator(string)
      An operator that specifies how to compare the specified terms in the terms parameter. Can be:

      • IN - posts from the specified terms (default).
      • NOT IN - posts from all terms except specified.
      • AND - posts simultaneously belonging to all specified terms.
      • EXISTS - posts from taxonomy (any term exists). In this case, you do not need to specify the terms parameter. Since version 4.1.
      • NOT EXISTS - posts not in taxonomy (no one term exists). In this case, you do not need to specify the terms parameter. Since version 4.1.

        Default: 'IN'

    • include_children(true/false)
      Include or not posts from child terms (for tree-like taxonomies). true - enable (include).
      Default: true

tax_query is an array of arrays. Each nested array defines the taxonomy query, its terms, and how to use these terms in the query.

This approach allows for querying multiple taxonomies. The relation operator specified in the first (outer) array of tax_query determines how the nested arrays should interact with each other.

#1 Retrieve posts from a single taxonomy

Get posts from the term bob, which is an element of people taxonomy:

$query = new WP_Query( [ 'people' => 'bob' ] );

#2 The same but with post type book_author

Get posts with book_author post type with the term bob, which is an element of people taxonomy:

$query = new WP_Query( [
	'post_type' => 'book_author',
	'people'    => 'bob'
] );

Let's do the same as in example above, but using the tax_query:

$query = new WP_Query( [
	'tax_query' => [
		[
			'taxonomy' => 'people',
			'field'    => 'slug',
			'terms'    => 'bob'
		]
	]
] );

#3 Output from multiple taxonomies

Get posts from custom taxonomies: people and language.

$query = new WP_Query( array(
	'post_type' => 'post',
	'people'    => 'bob',
	'language'  => 'english'
) );

Same as in the example above. Let's output the posts that also belong to several custom taxonomies using tax_query:

$query = new WP_Query( [
	'tax_query' => [
		'relation' => 'AND',
		[
			'taxonomy' => 'movie_janner',
			'field'    => 'slug',
			'terms'    => array( 'action', 'comedy' ),
		],
		[
			'taxonomy' => 'actor',
			'field'    => 'id',
			'terms'    => array( 103, 115, 206 ),
			'operator' => 'NOT IN',
		]
	]
] );

#4 Using the argument relation=OR

Output posts that are in the quotes category or that have the quote format (formats introduced in version 3.1). Use the relation argument:

$query = new WP_Query( [
	'post_type' => 'post',
	'tax_query' => [
		'relation' => 'OR',
		[
			'taxonomy' => 'category',
			'field' => 'slug',
			'terms' => [ 'quotes' ]
		],
		[
			'taxonomy' => 'post_format',
			'field' => 'slug',
			'terms' => [ 'post-format-quote' ]
		]
	]
] );

#5 Multi-level complex query using operator relation=OR

Let's say we want to get posts of one of the groups of taxonomy elements.

For example, we need to get cars of ford brand with black color or cars of bmw brand with white color.

In this example the first relation=OR operator affects on first child nested arrays. And relation=AND operator in nested arrays affects on arrays of nested arrays.

$query = new WP_Query(
	[
		'tax_query' => [
			'relation' => 'OR',
			[
				'relation' => 'AND',
				[
					'taxonomy' => 'brand',
					'field'    => 'slug',
					'terms'    => [ 'ford' ]
				],
				[
					'taxonomy' => 'color',
					'field'    => 'slug',
					'terms'    => [ 'black' ]
				]
			],
			[
				'relation' => 'AND',
				[
					'taxonomy' => 'brand',
					'field'    => 'slug',
					'terms'    => [ 'bmw' ]
				],
				[
					'taxonomy' => 'color',
					'field'    => 'slug',
					'terms'    => [ 'white' ]

				]
			]
		]
	]
);

$query = new WP_Query( $args );

Usage of get_posts()

#6 Print titles of post type countries from all sub-terms of the term 62, from the country taxonomy:

$args = array(
	'tax_query' => array(
		array(
			'taxonomy' => 'country',
			'field'    => 'id',
			'terms'    => array( 62 )
		)
	),
	'post_type' => 'countries',
	'posts_per_page' => -1
);
$posts = get_posts( $args );

foreach( $posts as $pst ){
	echo $pst->post_title .'<br>';
}

#7 Output all posts of article custom post type not related to the specified terms ('term-slug',' term-slug-two',' term-slug-three') of taxonomy (artictag):

// get all posts not related to the specified terms
$post_type = 'article';
$tax       = 'artictag';
$terms     = array('term-slug', 'term-slug-two', 'term-slug-three');

$psts  = get_posts( array(
	'posts_per_page' => -1,
	'post_type' => $post_type,
	'tax_query' => array(
			'relation' => 'AND',
			array(
				'taxonomy' => $tax,
				'terms' => $terms,
				'field' => 'slug',
				'operator' => 'NOT IN',
			)
		),
	'' => '',
) );

$i = 0;
foreach( $psts as $pst ){
	echo ++$i . ' <a href="'. get_permalink( $pst->ID ) .'">'. $pst->post_title .'</a><br>';
}

#8 Get all posts of custom post type func that are not in taxonomy func_cat

I.e. get func posts, with no one term from func_cat taxonomy. For doing this, we use operator=NOT EXISTS parameter.

$posts = get_posts( [
	'tax_query' => [
		[
			'taxonomy' => 'func_cat',
			'operator' => 'NOT EXISTS',
		]
	],
	'post_type' => 'func',
	'posts_per_page' => -1
] );

foreach( $posts as $pst ){
	echo $pst->post_title .'<br>';
}

#9 Get posts from taxonomy and sort by metafield

Get posts of type 'func' that are placed in any element of taxonomy 'tplcat' and sort these posts by numeric metafield 'views_prev_month'.

$query = new WP_Query( [
	'post_type'      => 'func',
	'posts_per_page' => -1,
	'tax_query' => [
		[
			'taxonomy' => 'tplcat',
			'operator' => 'EXISTS',
		]
	],
	'meta_key' => 'views_prev_month',
	'orderby'  => [ 'meta_value_num'=>'DESC' ],
] );

Author

Gets the posts belonging to certain authors.

author(int)
Author ID.
author_name(string)
The nickname of the author. You must specify a field value user_nicename.
author__in(array)
An array of IDs of the authors whose posts you want to retrieve.
author__not_in(array)
An array of IDs of the authors whose posts you want to exclude.

#1 Get posts for a single author

Posts by author ID:

$query = new WP_Query( 'author=123' );

Posts by author name (nickname):

$query = new WP_Query( 'author_name=rami' );

#2 Let's output posts of several authors at once

Posts of 4 authors by ID:

$query = new WP_Query( 'author=2,6,17,38' );

#3 Exclude posts by author

Get all posts except his posts except of author 12:

$query = new WP_Query( 'author=-12' );

#4 More example

Show all pages of author 1 (author=1), sort by title (orderby=title) in alphabetical order (order=ASC) and do not show sticky posts at the top.

$query = new WP_Query('caller_get_posts=1&author=1&post_type=page&post_status=publish&orderby=title&order=ASC');

#5 Several authors at the same time

Get all posts of authors with ID 2 and 6:

$query = new WP_Query( [ 'author__in' => [ 2, 6 ] ] );

Get posts of all authors, except authors with ID 2 and 6:

$query = new WP_Query( [ 'author__not_in' => [ 2, 6 ] ] );

Posts & Pages

Gets posts, pages and custom post types.

p(int)
ID of the post to be received. Ex: p=27.
name(string)
post_name field of post. Ex: name=about-site.
title(string)
Post title. Example: name=About Site. Since 4.4.
page_id(int)
ID of the static page you want to get. Ex: page_id=27.
pagename(string)
Name (post_name) of static page. Ex: pagename=about-site.
post_parent(int)
Returns child pages of specified post ID. By default, an empty string is not processed.
post_parent__in(string/array)
Select posts whose parents are specified in this array.
post_parent__not_in(string/array)
Select posts whose parents are not specified in this array.
post__in(string/array)

Specify the comma-separated ID of the posts you want to receive. Ex: post__in=5,12,2,14,7.

Note: if there are sticky posts, they will be included automatically. You can disable them by setting ignore_sticky_posts

Useful tip. If you output multiple posts via post__in, the publications are not displayed in the order you want, and the standard properties of orderby do not help, use orderby` => 'post__in.

post__not_in(string/array)
Displays all posts except specified.
post_name__in(string/array)
Select the specified posts. Specify post_name (slug) separated by commas or in an array. Since version 4.4.

NOTE: Ticket #28099: Passing an empty array to post__in, post_parent__in will return the last posts, but not an empty result. To get around this behaviour, you need to check the passed array in advance and if it is empty, don't run WP_Query.

Correct for WP version 6.4.

Get post by ID:

$query = new WP_Query( 'p=7' );

Get page by ID:

$query = new WP_Query( 'page_id=7' );

Get post by post_name (slug):

$query = new WP_Query( 'name=about-my-life' );

Get page by post_name (slug):

$query = new WP_Query( 'pagename=contact' );

Child posts / pages

Get child page using parent and current post_names separated with a slash: E.g: parent_slug/child_slug.

$query = new WP_Query( 'pagename=contact_us/canada&post_type=page' );

Get the child pages using the parent's ID:

$query = new WP_Query( 'post_parent=93&post_type=page' );

Get top-level pages, except all child pages:

$query = new WP_Query( 'post_parent=0&post_type=page' );

Output the posts whose parents are specified in the array:

$query = new WP_Query( [ 'post_parent__in' => [ 2, 5, 12, 14, 20 ] ] );

Multiple outputing of post/pages

Output only the specified posts:

$query = new WP_Query(
	[ 'post_type'=>'page', 'post__in' => [ 2, 5, 12, 14, 20 ] ]
);

Output all posts except the specified ones:

$query = new WP_Query( [
	'post_type'    => 'post',
	'post__not_in' => [ 2, 5, 12, 14, 20 ]
] );

Note: you cannot combine post__in and post__not_in in one request.

Do not use a comma-separated ID string, this will not work. You need to pass a ready array:

// WRONG
$exclude_ids = '1,2,3';
$query = new WP_Query( [ 'post__not_in' => [ $exclude_ids ] ] );

// CORRECT
$exclude_ids = [ 1, 2, 3 ];
$query = new WP_Query( [ 'post__not_in' => $exclude_ids ] );

Post Types

Get posts of the specified type.

post_type(string/array)

Posts of what type should be retrieved. Default: post. But if specify tax_query parameter, the default will be any.

Can be:

  • any - include all types, except revision, and types with exclude_from_search=true parameter specified.
  • attachment - by default WP_Query sets the status 'post_status'=>'publish'. But attachments has status 'post_status'=>'inherit', so to display attachments you need also to change post_status parameter to inherit or any.
  • page
  • post
  • revision
  • custom_type - name (slug) of any custom post type.
  • [ 'post', 'page' ] - several types specified in array.

Default: post

Posts by type

Get only pages:

$query = new WP_Query( 'post_type=page' );

Fetch all posts, except revisions and post types with registration parameter exclude_from_search = true:

$query = new WP_Query( 'post_type=any' );

Get 4 types of posts at the same time:

$query = new WP_Query( [
	'post_type' => [ 'post', 'page', 'movie', 'book' ]
] );

Custom fields (postmeta)

The main parameter for working with metadata in WP_Query is meta_query. It gets posts by their custom fields keys and values. Principle of meta_query usage is the same as tax_query — array, in which each element is child array with query parameters, i.e. meta_query is an array of arrays.

Such array design allows multiple requests. The first parameter relation in the main array describes the logical connection between the requests. It can take the values AND (used by default. Match all queries) or OR (match any query).

$args = [
	'meta_query' => [
		'relation' => 'OR',
		[
			'key' => 'color',
			'value' => 'blue'
		],
		[
			'key' => 'price',
			'value' => 20
		]
	]
];

Old meta data query variables: meta_key, meta_value, meta_value_num, meta_compare are still supported and can be used for simple queries related to postmeta fields.

List of all metadata-related parameters:

meta_key(string)
Key (name) of custom field.
meta_value(string)
Value of custom field.
meta_type(string)

Type of an custom field. The type of the value specified in meta_value parameter. This is needed to properly compare or sort values. Can be: NUMERIC, DECIMAL, SIGNED, UNSIGNED, CHAR, BINARY, DATETIME, DATE, TIME.

For more details read below in meta_query > type parameter.

meta_value_num(int)
Is meta value Numeric? If 1 (true) the values in meta_type parameter will be treated as NUMERIC.
meta_compare_key(строка) (с версии 5.1)
Operator to check value specified in meta_key parameter. Accepts: = or LIKE.
Default: '='
meta_compare(string)
Operator to check the specified value of custom field. Accepts: =, !=, >, >=, <, <=, LIKE, NOT LIKE, IN, NOT IN, BETWEEN, NOT BETWEEN, EXISTS (since 3.5), NOT EXISTS (3.5), REGEXP (3.7), NOT REGEXP (3.7) и RLIKE (3.7);
Default: '='
meta_type(string)

Type of an custom field. The type of the value specified in meta_value parameter. This is needed to properly compare or sort values. Can be: NUMERIC, DECIMAL, SIGNED, UNSIGNED, CHAR, BINARY, DATETIME, DATE, TIME.

For more details read below in meta_query > type parameter.

meta_query(array)

An array of meta-data parameters defining the output (this parameter includes meta_key, meta_value and meta_compare and replaces them). Since 3.1.

You can specify the following parameters in this array:

  • relation(string)
    This parameter is specified as a string in the main meta_query array and specifies how to compare several arrays (with query parameters) specified in this main array.
    This parameter accept two values:
    OR — select meta-fields suitable for any (at least one) query parameters array.
    AND (default) — select meta-fields suitable for all query parameters arrays.

    • key(string|array)
      Field Key. You can specify multiple keys in an array.
      Note: If you use regular expression here, when compare_key = REGEXP , you should NOT use / symbol at the edges.

    • compare_key(string) (since 5.1)
      How to compare the value specified in key. Can be:

      • = - equal.
      • != - is not equal. (WP 5.3)
      • LIKE - contains a specified substring.
      • NOT LIKE - does NOT contain a specified substring. (WP 5.3)
      • IN - is equal to one of the values. (WP 5.3)
      • NOT IN - is NOT equal to one of the values. (WP 5.3)
      • REGEXP - is equal to a regular expression. (WP 5.3)
      • NOT REGEXP - Does NOT match a regular expression. (WP 5.3)
      • RLIKE - synonymous of REGEXP. (WP 5.3)
      • EXISTS - alias of `=''. (WP 5.3)
      • NOT EXISTS - alias of `!=''. (WP 5.3)

      Default: '=', or 'IN', when the array is specified in $key

    • type_key(string) (since 5.3)
      The MySQL data type to use in CAST(). If specified, the value of column meta_key will first be converted to the specified type and only then will be compared to the specified value. It can be:

      • BINARY - needed when case of characters is important in regular expression.

      Default: ''

    • value(string/array)
      Meta value. Not necessary if compare parameter specified as: EXISTS or NOT EXISTS (since 3.9).
      Note: If you use regular expression here, when compare = REGEXP , you should NOT use / symbol at the edges.

    • compare(string)
      How to compare the value specified in value parameter. Accepts:

      • = — equally.
      • != — not equal.
      • > — greater than.
      • >= — greater than or equal to.
      • < — less than.
      • <= — less than or equal to.
      • LIKE — the substring specified in value parameter is present in meta value as '%value%' in sql query.
      • NOT LIKE — opposite meaning of LIKE parameter.
      • IN — you specify several values in value parameter (in array) and query search for at least one of specified values.
      • NOT IN — any value except those specified in array of value parameter.
      • BETWEEN — you specify 2 values (large and small) in value parameter (in array) and query search between specified values. Example: 'value' => [5, 25] — from 5 to 25 (inclusive).
      • NOT BETWEEN — any value outside the range specified in value. Example: 'value' => [5, 25] - less than 5 and more than 25.
      • EXISTS — retrieve all posts with meta_key (specified in key parameter). 'value' parameter no need to be specified in this case. (since 3.5)
      • NOT EXISTS — retrieve all posts, except posts with meta_key (specified in key parameter). 'value' parameter no need to be specified in this case. (since 3.5).
      • REGEXP — you specifies a search regular expression in value parameter. Example: 'value'=>'^bar', (match: 'bar is'). (since 3.7)
      • NOT REGEXP — in value you specify a regular expression to search for. Gets everything that is not match to this expression. (since 3.7)
      • RLIKE — a synonym for REGEXP. (since 3.7)
    • Default: 'IN' if value is an array. '=' in other cases.*

    • type(string)
      Type of meta value (for example, if meta contains only numbers we need to specify here NUMERIC, in order to not sort numbers values as strings).

      Specified here type directly passes to mysql CAST() function.

      Can be:

      • NUMERIC — integer. You can specify the length DECIMAL(p,s).
      • DECIMAL — float number. You can specify the length DECIMAL(p,s). For example: DECIMAL(5,2) - number with maximum 5 digits (3 before separator) and 2 digits after separator.
      • SIGNED — integer (positive and negative)
      • UNSIGNED — integers (only positive)
      • CHAR — string (case-insensitive)
      • BINARY — string (case-sensitive)
      • DATETIME — date and time
      • DATE — only the date
      • TIME — only time

      If specify any other type (or don't specify type) then the type will be CHAR.

      Default: CHAR

    The DATE type works with BETWEEN comparation only if the date is specified in the YYYY-MM-DD format and compared with the same format.

Sort by custom field

To sort by meta field, you need to specify the field data key, and then specify this key in the orderby parameter. Example:

'meta_query' => [
	'book_color' => [
		'key'     => 'color',
		'value'   => 'blue',
		'compare' => 'NOT LIKE',
	],
],
'orderby' => 'book_color',
'order'   => 'DESC',

If custom field is specified using the meta_key and meta_value parameter, specify the following values in the orderby parameter: meta_value or meta_value_num (for numbers). See description of the orderby parameter.

'meta_key=color&meta_value=blue&orderby=meta_value&order=ASC'

Without meta_query examples

#1 Posts with meta field key color, value doesn't matter:

$query = new WP_Query( 'meta_key=color' );

#2 Posts with blue value of meta field, name of the key doesn't matter:

$query = new WP_Query( 'meta_value=blue' );

#3 Posts with color meta field and blue value of this field:

$query = new WP_Query( array( 'meta_key' => 'color', 'meta_value' => 'blue' ) );

#4 Posts with color meta key and the field value is not equal to blue:

$query = new WP_Query(
	 array( 'meta_key' => 'color', 'meta_value' => 'blue', 'meta_compare' => '!=' )
);

#5 Products with price meta key and meta value less than or equal to 22

If use "meta_value" parameter, the value of 99 will be greater than 100 because these values are recognized as strings (not numbers). For numbers to be interpreted correctly, use the "meta_value_num" parameter":

$query = new WP_Query(
	 [ 'meta_key' => 'price', 'meta_value_num' => '22', 'meta_compare' => '<=', 'post_type' => 'product' ]
);

#6 Posts with custom field value equal to 0 (no matter what the key)

$query = new WP_Query( array ( 'meta_value' => '_wp_zero_value' ) );

meta_query usage examples

#1 Posts with one custom field

Output products (product) with color meta key and not blue value:

$query = new WP_Query( [
	'post_type'  => 'product',
	'meta_query' => [
		[
			'key'     => 'color',
			'value'   => 'blue',
			'compare' => 'NOT LIKE'
		]
	]
] );

Note: for meta_query you must specify an array in an array, even if you specify a single query.

#2 Posts that have two custom fields at the same time

Get posts with several custom fields and specify different conditions for the values of these custom fields:

$query = new WP_Query( [
	'post_type'  => 'product',
	'meta_query' => [
		[
			'key'     => 'color',
			'value'   => 'blue',
			'compare' => 'NOT LIKE'
		],
		[
			'key'     => 'price',
			'value'   => [ 20, 100 ],
			'type'    => 'numeric',
			'compare' => 'BETWEEN'
		]
	]
] );

#3 Posts with at least one of the fields

Get posts with color meta key and value not (LIKE) blue or (OR) posts with price key with field values from 20 to 100:

$query = new WP_Query( [
	'post_type'    => 'product',
	'meta_query'   => [
		'relation' => 'OR',
		[
			'key'     => 'color',
			'value'   => 'blue',
			'compare' => 'NOT LIKE'
		],
		[
			'key'     => 'price',
			'value'   => [ 20, 100 ],
			'type'    => 'numeric',
			'compare' => 'BETWEEN'
		]
	]
] );

#4 Posts with two fields sorted by a third one

Suppose we have a toys post type, and in custom fields we specify the material: strong, soft toy and toy weight in grams: 100 300 500.

Get soft toys whose weight from 200 to 500 grams and sort them by price in descending order:

$query = new WP_Query( [
	'post_type'  => 'toys',
	'meta_query' => [
		[
			'key'   => 'type',
			'value' => 'soft',
		],
		[
			'key'     => 'weight',
			'value'   => [ 200, 500 ],
			'compare' => 'BETWEEN',
			'type'    => 'NUMERIC',
		],
	],
	'meta_key' => 'price',
	'orderby'  => 'meta_value_num',
	'order'    => 'DESC'
] );

#5 Get posts with specified custom field

This example shows how to get posts for which thumbnails are set (which have custom field _thumbnail_id)):

$args = array(
	'posts_per_page' => 3,
	'post_type'  => 'post',
	'meta_query' => array(
		array(
			'key'     => '_thumbnail_id',
			'compare' => 'EXISTS'
		)
	)
);
$query = new WP_Query( $args );

#6 Nested complex queries

Since 4.1, you can create subqueries. For example, let's mutch following condition: show products of orange color (color=orange) OR small (size=small) products of red color (color=red):

$args = [
	'post_type'  => 'product',
	'meta_query' => [
		'relation' => 'OR',
		[
			'key'     => 'color',
			'value'   => 'orange',
			'compare' => '=',
		],
		[
				'relation' => 'AND',
				[
						'key'     => 'color',
						'value'   => 'red',
						'compare' => '=',
				],
				[
						'key'     => 'size',
						'value'   => 'small',
						'compare' => '=',
				],
		],
	],
];
$query = new WP_Query( $args );

#7 Sort by custom field

Since 4.2, you can specify keys in meta_query and then sort by them:

$args = [
	'post_type'  => 'book',
	'meta_query' => [
		'book_color' => [
			'key'     => 'color',
			'value'   => 'blue',
			'compare' => 'NOT LIKE',
		],
	],
	'orderby' => 'book_color',
];
$query = new WP_Query( $args );

Post MIME Type

Selects attachments with specified MIME type. See wp_get_mime_types() for a full list of MIME types.

post_mime_type(string/array)

What type of attachment you want to get. You can use this parameter when the post_type = attachment and post_status = inherit only. I.e. only attachments have MIME-type.

You can specify several mime types in an array instead of just one. List of mime types:

  • image/jpeg
  • image/png
  • image/gif
  • image - для любых картинок
  • audio/mpeg
  • application/pdf
  • application/zip
  • See here for a complete list of mime types.

Get only pictures of any type

$query = new WP_Query( array(
	'post_mime_type' => 'image',
	'post_type'      => 'attachment',
	'post_status'    => 'inherit',
) );

Get only pictures of the gif type

$query = new WP_Query( array(
	'post_mime_type' => 'image/gif',
	'post_type'      => 'attachment',
	'post_status'    => 'inherit',
) );

Get all types of attachments except images

$query = new WP_Query( [
	'post_mime_type' => [ 'application', 'text', 'video', 'audio' ],
	'post_type'      => 'attachment',
	'post_status'    => 'inherit',
] );

Post Status

Receives posts with the specified status.

post_status(string/array)

The status of the post.

Default is publish, and if user is authorized the private status is also added. If the request is launched from the admin part protected status types are added too: future, draft and pending.

All status types:

  • publish - published post.
  • pending - post on moderation.
  • draft - draft posts.
  • auto-draft - draft saved by WordPress (auto-save).
  • future - scheduled post.
  • private - personal post.
  • inherit - revision or attachment.
  • trash - removed post (in trash). Since 2.9.
  • any - all statuses, except status with parameter exclude_from_search=true. See register_post_status() and get_post_stati().

Default: 'publish'

Posts by status

Get only drafts:

$query = new WP_Query( 'post_status=draft' );

Get posts with different statuses:

$query = new WP_Query(
	 [ 'post_status' => [ 'pending', 'draft', 'future' ] ]
);

Get attachments with any post status:

$query = new WP_Query(
	  [ 'post_status' => 'any', 'post_type' => 'attachment' ]
);

Date (time)

Displays posts belonging to a certain period of time.

year(int)
4 year digits (e.g. 2013)
monthnum(int)
The number of the months (1 - 12)
w(int)
Week of the year (from 0 to 53)
day(int)
Day of month (1 - 31)
hour(int)
Hour (0 - 23)
minute(int)
Minute (0 - 60)
second(int)
Second (0 - 60)
m(int)
YearMonth (e.g. 201306)
date_query(array)

Date parameters to build query. Works based on WP_Date_Query class.

This parameter is specified as an array that can contain nested arrays. Parameters: column, compare, relation for the main array work as default parameters for nested arrays (if any).

For possible date formats, see PHP documentation.

  • column — database field for the query. Can be:

    post_date
    post_date_gmt
    post_modified
    post_modified_gmt
    comment_date
    comment_date_gmt
    user_registered

    Default: 'post_date'

  • compare — A comparison operator for all nested arrays by default. Can be: =, !=, >, >=, <, <=, IN, NOT IN, BETWEEN, NOT BETWEEN.
    Default: '='

  • relation — operator, if there are multiple dates arrays:
    AND (take into account simultaneously all the specified arrays).
    OR (if there is a match with at least one specified array).
    Default: OR

    The parameters below should be used in nested arrays. They define a query for a single date. All parameters below can be used in the main array as well.

    • before(string/array) — The date "to" which posts will be received. Takes a string that understand strtotime() function: all possible formats.
      Or you can pass an array with indexes: year, month, day: [ 'year'=>'2015', 'month'=>'5', 'day'=>'28' ]
      Relative to the current site time (not UTC).

    • after(string/array) — The date "after" which posts will be received. Takes a string that understand strtotime() function: all possible formats.
      Or you can pass an array with indexes: year, month, day: [ 'year' => '2015', 'month' => '5', 'day' => '28' ]
      Relative to the current site time (not UTC).

    • column(string) — see above. For specific date only. Default: the value of the upper array.

    • compare(string) — see above. For specific date only. Default: '='.

    • inclusive(true/false) — if true the before and after arguments are processed inclusive. Default: false.

    • year(int/array) — year, for example 2013.
    • dayofyear(int/array) — day of the year, 1-366.
    • month(int/array) — month, 1-12
    • week(int/array) — week, 0-53
    • day(int/array) — day, 1-31
    • dayofweek(int/array) — day of the week, 1-7, where 1 is Sunday.
    • dayofweek_iso(int/array) — day of the week, 1-7, where 1 is Monday.
    • hour(int/array) — hour, 0-23
    • minute(int/array) — minute, 0-60
    • second(int/array) — second, 0-60

    In parameters: year, month, week, dayofyear, day, dayofweek, dayofweek_iso, hour, minute, second you can specify multiple values as an array if the compare parameter allow.

#1. Get posts for today:

$today = getdate();
$query = new WP_Query( [
	'year'     => $today['year'],
	'monthnum' => $today['mon'],
	'day'      => $today['mday'],
] );

#2. Get posts for the last week:

$week = date('W');
$year = date('Y');
$query = new WP_Query( 'year=' . $year . '&w=' . $week );

#3. Get posts for 20 December:

$query = new WP_Query( 'monthnum=12&day=20' );

#4. Get posts over a period of time from 1 March to 15 March 2010:

// Create a new function that adds a where clause to the query
function where_clause__filter( $where = '' ){
	// from March 1 to March 15, 2010
	$where .= " AND post_date >= '2010-03-01' AND post_date < '2010-03-16'";
	return $where;
}

add_filter( 'posts_where', 'where_clause__filter' );
$query = new WP_Query( $query_string );
remove_filter( 'posts_where', 'where_clause__filter' );

Examples above retrieve posts for the specified period of time (in history): "Posts for x month, x day". They can't retrieve posts for any period of time in relation to the current time. Therefore, requests such as "Posts for the last 30 days" or "Posts for the last year" are not possible in the basic version. For such requests, you need to use posts_where filter. The examples below show how to do this.

#5. Get posts for the last 30 days:

// Create a new function that adds a where clause to the query
function where_clause__filter( $where = '' ) {
	// for the last 30 days
	$where .= " AND post_date > '" . date('Y-m-d', strtotime('-30 days')) . "'";
	return $where;
}

add_filter( 'posts_where', 'where_clause__filter' );
$query = new WP_Query( $query_string );
remove_filter( 'posts_where', 'where_clause__filter' );

#6. Get posts for the interval from 30 to 60 days, up to the present:

// Create a new function that adds a where clause to the query
function where_clause__filter( $where = '' ) {
	// 30 to 60 days
	$where .= " AND post_date >= '" . date('Y-m-d', strtotime('-60 days')) . "'" . " AND post_date <= '" . date('Y-m-d', strtotime('-30 days')) . "'";
	return $where;
}

add_filter( 'posts_where', 'where_clause__filter' );
$query = new WP_Query( $query_string );
remove_filter( 'posts_where', 'where_clause__filter' );

m parameter can be set only for posts list in the admin panel. It is useful when you select from the select drop-down list where the date is set as YYYYmm number.

Examples with date_query parameter

#1. Get posts between 9 and 17 hours:

$args = [
	'date_query' => [
		[
			'hour'      => 9,
			'compare'   => '>=',
		],
		[
			'hour'      => 17,
			'compare'   => '<=',
		],
		[
			'dayofweek' => [ 2, 6 ],
			'compare'   => 'BETWEEN',
		],
	],
	'posts_per_page' => -1,
];
$query = new WP_Query( $args );

#2. Get posts for the time period: 1 January to 28 February:

Dates can be specified as numbers and strings, because the arguments before and after are handled by the PHP function strtotime(), it understands date in a string format.

$args = [
	'date_query' => [
		[
			'after'     => 'January 1st, 2013',
			'before'    => [
				'year'  => 2013,
				'month' => 2,
				'day'   => 28,
			],
			'inclusive' => true,
		],
	],
	'posts_per_page' => -1,
];
$query = new WP_Query( $args );

#3. Get posts published a year ago, but changed in the last month:

$args = [
	'date_query' => [
		[
			'column' => 'post_date_gmt',
			'before' => '1 year ago',
		],
		[
			'column' => 'post_modified_gmt',
			'after'  => '1 month ago',
		],
	],
	'posts_per_page' => -1,
];
$query = new WP_Query( $args );

#4. Get posts for the last 2 weeks using the string '2 weeks ago':

$query = new WP_Query( [
	'date_query' => [
		'after' => '2 weeks ago',
	],
] );

#5. Get posts for the last 2 months, published on weekdays:

$query = new WP_Query( [
	'date_query' => [
		'after' => '2 months ago',
		[
			'dayofweek' => [ 1, 5 ],
			'compare' => 'BETWEEN',
		],
	],
] );

The date_query argument works with the WP_Comment_Query class as well, so it can be used in get_comments() function.

Sort & Order

Sorts and sets the sort direction.

Sorting parameters not work if you specify fields = ids, because in this case, the query will not have fields by which it can sort the result.

order(string)

The sort direction for the orderby parameter. Can be:

  • ASC - in order, from less to more (1, 2, 3 or a, b, c).
  • DESC - in reverse order, from more to less (3, 2, 1 or c, b, a).
orderby(string/array)

Fields on which you can sort posts. Can be:

  • none — do not sort, output as is in the database. Equivalent to sorting by ID. Since 2.8.
  • ID — sort by ID.
  • author — sort by author ID.
  • title — sort by title.
  • name — by post name (post slug).
  • date — sort by publication date.
  • modified — sort by modification date.
  • type — by post type (post_type). Since 4.0.
  • parent — sort by the value of the post_parent field.
  • rand — random order.
  • RAND(x) — in a random order for numeric values. Here "x" is an integer.
  • comment_count — sort by the number of comments. Since 2.9.
  • relevance — by search terms (parameter s) in the following order: 1) whether the entire sentence is matched. 2) all search terms are in the post title. 3) any of the search terms appear in the post title. 4) the full sentence is found in the post content.
  • menu_order — used for pages and attachments. The sequence number is specified on the edit-post admin page.
  • meta_value — by the value of a custom field.

    Important: the meta_key parameter must also be defined. Note: the sorting will be alphabetical and it's not logical if the values of custom field is a numbers (for example 1, 3, 34, 4, 56, 6 etc., not 1, 3, 4, 6, 34, 56).

  • meta_value_num — sort by custom field whose values are numbers. Since 2.8.
  • array key from meta_query — in this case, the sorting will be based on the value of custom field specified in the meta_query array.
  • post__in — takes into account the order of ID specified in post__in parameter. order parameter is ignored
  • post_name__in — takes into account the order of names specified in the post_name__in parameter. order parameter is ignored
  • post_parent__in — takes into account the order of ID specified in post_parent__in parameter. order parameter is ignored.

The prefix post_ in table fields is omitted for convenience. For example, instead of date you can write post_date, instead of content post_content etc.

orderby = array()

Since WordPress 4.0 you can specify the combination of orderby and order parameters as an array in orderby parameter. It is for sorting by several columns simultaneously. Syntax is:

'orderby' => array( 'title' => 'DESC', 'menu_order' => 'ASC' )

#1 Sort by title

$query = new WP_Query( [ 'orderby' => 'title', 'order' => 'DESC' ] );

Sort by menu order and then by title

$query = new WP_Query( [ 'orderby' => 'menu_order title', 'order' => 'DESC' ] );

#2 Output one random post:

$query = new WP_Query( [ 'orderby' => 'rand', 'posts_per_page' => '1' ] );

#3 Sort posts by number of comments:

$query = new WP_Query( [ 'orderby' => 'comment_count' ] );

#4 Sort products by price (price custom field):

$query = new WP_Query(
	 array ( 'post_type' => 'product', 'orderby' => 'meta_value', 'meta_key' => 'price' )
);

#5 Multiple sorting

Output posts sorted by two fields: 'title' and 'menu_order' (title is paramount):

$query = new WP_Query(
	array( 'post_type' => 'page', 'orderby' => 'title menu_order', 'order' => 'ASC' )
);

#6 Multiple sorting using an array

Get pages sorted by title (post_title) and menu number (menu_order) in different order (ASC/DESC):

$query = new WP_Query( [ 'orderby' => array( 'title' => 'DESC', 'menu_order' => 'ASC' ) ] );

#7 Sorting by 'meta_value' for custom post type

Output posts of 'my_custom_post_type' type sorted by 'age' custom field and filtered to show posts with custom field value 3 and 4:

$args = [
   'post_type'  => 'my_custom_post_type',
   'meta_key'   => 'age',
   'orderby'    => 'meta_value_num',
   'order'      => 'ASC',
   'meta_query' => [
	   [
		   'key'     => 'age',
		   'value'   => [ 3, 4 ],
		   'compare' => 'IN',
	   ]
   ]
];
$query = new WP_Query( $args );

#8 Sorting by multiple meta_keys

To order by two different custom fields (for example, city first and state second), you need to set array keys for meta_query sub-arrays and then use this keys in orderby array:

$query = new WP_Query( [
	'meta_query' => [
		'relation'     => 'AND',
		'state_clause' => [
			'key'   => 'state',
			'value' => 'Wisconsin',
		],
		'city_clause' => [
			'key'     => 'city',
			'compare' => 'EXISTS',
		],
	],
	'orderby' => [
		'city_clause'  => 'ASC',
		'state_clause' => 'DESC',
	],
] );

Pagination & Offset

nopaging(true/false)
Turn off pagination, displays all posts on one page.
posts_per_page(int)

Number of posts per page. If set "-1", all posts will be displayed (without pagination).

Since 2.1 replaces the showposts parameter. Set the paged parameter if pagination does not work after using this parameter.

Note: if the query made in the feed part, WP overwrites this parameter with the posts_per_rss option. To affect the output of the feed, use post_limits or pre_option_posts_per_rss filters.

posts_per_archive_page(int)
Number of posts for archive pages: for pages that meet the conditions is_archive() or is_search(). This option overwrites parameters posts_per_page and showposts.
offset(int)

How many posts to skip from the top of the selection (top indent). For example, by default the query returns 5 last posts, and if you add the parameter offset=1 into the same query, it will also return 5 posts, but the one last post will be skipped.

This parameter overrides the paged parameter and can break pagination. If this happens read solution to the problem.

paged(int)
Pagination page number. Shows posts that normally have to be shown on X pagination page. Overwrites posts_per_page parameter.
page(int)
Pagination page number for the static home page. Shows posts that normally have to be shown on X pagination page of the main static page (front page).
ignore_sticky_posts(true/false)

Ignore sticky posts or not (true/false).

Sticky posts will not appear at the top of the list, but they are not excluded and will be shown in usual order.

#1 Posts per page

Get 3 posts:

$query = new WP_Query( 'posts_per_page=3' );

Get all posts:

$query = new WP_Query( 'posts_per_page=-1' );

Get all posts and disable pagination:

$query = new WP_Query( 'nopaging=true' );

#2 The indentation from the top (offset)

Get posts starting with the fourth (skip the first 3):

$query = new WP_Query( 'offset=3' ) );

Get 5 posts that follow after first 3 posts:

$query = new WP_Query( [ 'posts_per_page' => 5, 'offset' => 3 ] );

Let's skip the first/one post (offset=1) and return the next 5:

$query = new WP_Query( 'posts_per_page=5&offset=1' );

#3 Posts from the pagination page 6

$query = new WP_Query( 'paged=6' );

#4 Posts from current pagination page

Useful for building custom pagination:

$query = new WP_Query( [ 'paged' => get_query_var( 'paged' ) ] );

Get posts from the current pagination page and set the paged parameter to 1 when the variable is not defined on the first page of pagination:

$paged = get_query_var('paged') ?: 1;
$query = new WP_Query( [ 'paged' => $paged ] );

Use get_query_var('page'); if you want to get the pagination page number for a static home page (is_front_page()). The "page" query variable contains pagination page number, when the <!--nextpage--> tag is used in post content to split post content.

Get posts of pagination page for static home page:

$paged = get_query_var('page') ?: 1;
$query = new WP_Query( [ 'paged' => $paged ] );

#5 Sticky posts

#5.1 All sticky posts
$sticky = get_option( 'sticky_posts' );
$query = new WP_Query( [ 'post__in' => $sticky ] );
#5.2 Only first sticky post
$sticky = get_option( 'sticky_posts' );
$query = new WP_Query( 'p=' . $sticky[0] );
#5.3 First sticky post. If there is no sticky post, then the last published
$query = new WP_Query( array(
	'posts_per_page'      => 1,
	'post__in'            => get_option( 'sticky_posts' ),
	'ignore_sticky_posts' => 1
) );
#5.4 First sticky post. If there is no sticky post, then nothing is output
$sticky = get_option( 'sticky_posts' );

if ( ! empty($sticky[0]) ) {

	$query = new WP_Query( array(
		'posts_per_page'      => 1,
		'post__in'            => $sticky,
		'ignore_sticky_posts' => 1
	) );

	// output...
}
#5.5 last sticky posts
$sticky = get_option( 'sticky_posts' ); // all Sticky posts

rsort( $sticky ); // sort - new at the top

$sticky = array_slice( $sticky, 0, 5 ); // get the first 5

$query = new WP_Query( [ 'post__in'=>$sticky, 'ignore_sticky_posts'=>1 ] );

#5.6 Hide sticky posts

Exclude all sticky posts from the query:

$query = new WP_Query( [ 'post__not_in' => get_option( 'sticky_posts' ) ] );

Exclude sticky posts from the category. The following example returns all posts from the category, but sticky posts will be shown as regular posts (by date):

$query = new WP_Query( [ 'ignore_sticky_posts' => 1, 'posts_per_page' => 3, 'cat' => 6 ] );

Exclude sticky posts from the category. Next example return all posts from the category, and sticky posts will be excluded. Also, add a rule for proper pagination:

$paged  = get_query_var( 'paged' ) ?: 1;
$sticky = get_option( 'sticky_posts' );
$query  = new WP_Query( array(
	'cat'                 => 3,
	'ignore_sticky_posts' => 1,
	'post__not_in'        => $sticky,
	'paged'               => $paged
) );

Comment

The parameters below allow to get posts by some comments data. Also, comments data are added into the WP_Query object if the query made for a single post; and some parameters below control such comments.

comment_status(string)
Status of the comment.
ping_status(int)
Status of the ping.
comments_per_page(int)
Number of comments to retrieve for a separate comment page. By default value of WP option comments_per_page.
comment_count(int/array)

Number of comments retrieved posts should have. Since 4.9.

  • Number — if specify a number, query retrieve posts with the specified number of comments (search operator =).
  • Array — you can specify an array, it allows you to set option how to compare specified number. Allowed array keys:
    • value(int) — number of comments posts should have.
    • compare(string) — how to compare specified number. Can be: = (default), !=, >, >=, <, <=.
  • The default search is based on the fields post_title, post_excerpt, post_content.
  • If a search phrase consists of more than 10 words, it is treated as a sentence sentence=true.
s(string)

Search phrase. A phrase consisting of several words is divided into separate words by default. Each word is searched separately in this form: word1 OR word2 OR ....

To separate words are used the \t ",+ characters (tab, space, comma, plus, double quote).

A regular expression that splits a phrase into words:

".*?("|$)|((?<=[\t ",+])|^)[^\t ",+]+

SQL part of search query is created by WP_Query::parse_search() method, it also split the phrase into words.

Each individual word (term) is then processed by WP_Query::parse_search_terms().

  • Symbol " — words or phrase framed in double quotes will participate in the search as is.

    For example, we want to find the word station, by default the records containing the word electric-station will be found, but if you specify the search query in quotes and add a space at the beginning, then this space will be used in the search " station".

    Or we can search by exact phrase occurrence by framing the desired part of the search phrase or the whole phrase in double quotes (for the whole phrase you can simply add quotation marks to the beginning of the phrase), for example:

    "WP Search is not that simple   →  WP Search is not that simple
    "WP Search is not that simple"  →  WP Search is not that simple
     WP Search is "not that simple" →  WP | Search | is | not that simple
    "WP Search is" not that simple  →  WP Search is | not | that | simple
  • Symbol - — The word exclusion symbol. Adding a hyphen at the beginning of the search word will indicate that the word should not be in the search text. For example: pillow -sofa will return posts containing pillow but not containing the word sofa.

    The exclusion character can be changed through the filter wp_query_search_exclusion_prefix.

  • Word Filtering - dashes, single letters a-z and stop words are removed from search terms (words) after splitting search phrase into terms (words).

    Stop words are determined by WP_Query::get_search_stopwords(). By default they include a localized list of the following words:

    _x( 'about,an,are,as,at,be,by,com,for,from,how,'.
    	'in,is,it,of,on,or,that,the,this,to,was,what,'.
    	'when,where,who,will,with,www',
    	'Comma-separated list of search stopwords in your language'
    )

    You can change the list of these stop words through the filter wp_search_stopwords.

exact(logical)

true - do not add % to the ends of search phrases in the SQL query. Set true here makes sense if sentence=true and we need to find the post in the title/content/excerpt of which is exactly the specified phrase. For example, 'looking for such a phrase' will be handled so = 'looking for such a phrase', but not LIKE '%looking for such a phrase%'.

Default: false

sentence(true/false)

true - search for the full search phrase (not split it to separate words). 'seeking such a phrase' will be processed by LIKE '%seeking such a phrase%'.
false - the search phrase is divided into words and the search is performed by words.

Default: false

#1 Search posts with words

Search result returns posts which contains all of the words search and phrase.

$query = new WP_Query( 's=search phrase' );

#2 Search posts by sentence

Search result returns posts which contains sentence search phrase.

$query = new WP_Query( 's=search phrase &sentence=1' );

#2 Exclude words

Search result returns posts which contains word search and not contains word phrase.

$query = new WP_Query( 's=search -phrase' );

Returned fields

Sets which fields the query should return.

fields(string)

What fields to return. By default, all fields are returned.

  • ids - returns an array with post ID.
  • id=>parent - returns an associative array [ parent => ID, … ].
  • '' - returns array of WP_Post objects.
  • Specifying any other value will return all fields (default) - array of WP_Post objects.
no_found_rows(true/false)

true — don't count the number of found rows.
false - adds SELECT SQL_CALC_FOUND_ROWS to the query.

In some cases, it may speed up the request.

Default: false

Cache

The parameters below control the data addition to the object cache when executing queries.

cache_results(true/false)
Whether to cache information about the post.
Default: true
update_post_meta_cache(true/false)
Whether to cache information about postmeta data.
Default: true
update_post_term_cache(true/false)
Whether to cache information aboute post terms and taxonomies.
Default: true
lazy_load_term_meta(true/false)
Whether to lazy-load term meta. Setting to false will disable cache priming for term meta, so that each get_term_meta() call will hit the database.
Default: value of $update_post_term_cache
update_menu_item_cache(true/false) (WP 6.1)
Whether to update the menu item cache.
Default: false

#1 Get 50 posts, but don't add information about posts to the cache:

$query = new WP_Query( [ 'posts_per_page' => 50, 'cache_results' => false ] );

#2 Get 50 posts, but don't add meta data of the post to the cache:

$query = new WP_Query( [ 'posts_per_page' => 50, 'update_post_meta_cache' => false ] );

#3 Get 50 posts, but don't add information about the terms of posts to the cache:

$query = new WP_Query( [ 'posts_per_page' => 50, 'update_post_term_cache' => false ] );

Usually, this functionality do not need to be used — the cache is necessary! However, this approach can be useful in some cases. For example, if you need to get a list of post titles and don't need any other information about posts: neither about taxonomies nor about meta data. You can avoid unnecessary SQL queries if do not load this unnecessary information.

Note: if a persistent cache plugin is used, all these parameters are set to false by default because there is no need to update the cache every time the page loads.

Filters

suppress_filters(true/false)

Whether to suppress filters — disable work of some hooks of WP_Query class. Enabling this parameter cancels all SQL query modification filters.

uppress_filters = true does not affect the pre_get_posts filter.

true — turn off handling of following filters:

posts_search
posts_search_orderby

posts_where
posts_join

posts_where_paged
posts_groupby
posts_join_paged
posts_orderby
posts_distinct
post_limits
posts_fields
posts_clauses

posts_where_request
posts_groupby_request
posts_join_request
posts_orderby_request
posts_distinct_request
posts_fields_request
post_limits_request
posts_clauses_request

posts_request
posts_results
the_posts

comment_feed_join
comment_feed_where
comment_feed_groupby
comment_feed_orderby
comment_feed_limits

Default: false

Permission

Get the posts if the current user has sufficient permissions.

perm(string)
User capability.

Output published private posts, if the user has sufficient rights to view:

$query = new WP_Query(
	  [ 'post_status' => [ 'publish', 'private' ], 'perm' => 'readable' ]
);

Parameters Combination

#1. Combining in a string

You may have noticed in some examples above that parameters are joined by the ampersand symbol - &. By this symbol, parameters can be joined (combined).

Posts from category 3 for 2004 year:

$query = new WP_Query( 'cat=3&year=2004' );

Get all posts from category 1 with the apples tag:

$query = new WP_Query( 'cat=1&tag=apples' );

#2. Adding parameters to the query

Get posts from category 13 for the current month on the main site page:

if ( is_home() ) {
	$query = new WP_Query( $query_string . '&cat=13&monthnum=' . date( 'n', current_time('timestamp') ) );
}

#3. Parameters in an array

Get 2 posts, from category 1 and 3, sorted in reverse order by title:

$query = new WP_Query( [
   'category__and'  => [ 1,3 ],
   'posts_per_page' => 2,
   'orderby'        => 'title',
   'order'          => 'DESC',
] );

Examples

#1. Exclude posts from the category 3 on the main page of the site

To exclude posts that are in category 3 on the main page of the blog, you need to insert the following code into the index.php file before the WordPress Loop:

<?php
if ( is_home() ) {
	query_posts( $query_string .'&cat=-3' );
}
?>

#1.2. You can add more categories:

<?php
if ( is_home() ) {
	query_posts( $query_string .'cat=-1,-2,-3' );
}
?>

#2. Get a certain post (post with ID = 5):

<?php
// retrieve one post with an ID of 5
$query = new WP_Query( 'p=5' );
?>

#3. If you want to use the read more function with a new query, you need switch the global variable $more to 0:

<?php
// retrieve one post with an ID of 5
$query = new WP_Query( 'p=5' );

global $more;
// set $more to 0 in order to only get the first part of the post
$more = 0;

// the Loop
while ( have_posts() ) : the_post();
  // the content of the post
  the_content( 'Read the full post »' );
endwhile;
?>

#4. Get a specific page (page 7):

<?php
$query = new WP_Query('page_id=7');      // get only page with ID 7
?>

or

<?php
$query = new WP_Query('pagename=aboute-site'); // get only the "aboute-site" page
?>

#4.1. For child pages, you need specify the name of the parent page and the child page itself.

Names are separated by a slash (/). Example:

<?php
$query = new WP_Query( 'pagename=aboute-site/authors'); // get page "authors", which is child page of the "aboute-site"
?>

Inserting variables into query parameters

You can create dynamic query parameters, if you need change query depending on any circumstances. To do this save parameter value to a variable, and then pass this variable to the query parameters. You can do it in several ways:

#1. Building a query using single quotes ' ':
<?php
$category_var = $cat;
$query = 'cat=' . $category_var . '&orderby=date&order=ASC'; // collect query
$query = new WP_Query( $query ); // run a query
?>
#2. Building a query using double quotes " "

Variables inside double quotes are interpreted by PHP as variables, not as plain text:

<?php
$current_month = date('m');
$current_year  = date('Y');

$query = new WP_Query( "cat=22&year=$current_year&monthnum=$current_month&order=ASC" );
?>
<!-- WordPress Loop here -->
#3. Using global variable $query_string

That contains a basic query for the query_posts() function. If you don't need to break the standard WordPress posts output (for example, on the category page), but need to remove the pagination (display all posts on one page), we can extend the basic query_posts() parameters with posts_per_page=-1:

<?php
global $wp_query;
$wp_query = new WP_Query( $query_string . '&posts_per_page=-1' );
while( have_posts() ){
	the_post();
	// here is the WordPress Loop
}

wp_reset_query(); // reset global $wp_query
?>

You can change the value of posts_per_page to a specific number of posts you need on a single page. For example, posts_per_page=10 displays only 10 posts, and if you put the template tag posts_nav_link() at the end of the loop, a link will appear below the loop to go to the next 10 posts (pagination link).

#4. Parameters can also be passed as an array

Specifying parameters as an array is more readable. Example 2 can be written as:

$query = new WP_Query( array(
	'cat'      => 22,
	'year'     => $current_year,
	'monthnum' => $current_month,
	'order'    => 'ASC',
) );

As you can see you can put each variable on a separate line, and it is more readable.

Adding parameters to a query

Save the base query of the current page and add your own parameters to it.

The query_posts() function completely overwrites the main WP query, if, for example, we write query_posts('cat=1'), then other query parameters that are used for the current page (for example, sorting, pagination, etc.) will be lost and posts of category 1 will be displayed only. To save the basic parameters of the query and add/replace them with your own, you need to use array_merge() PHP function (combines 2 arrays into one):

global $wp_query;
query_posts(
	array_merge(
		array( 'cat' => 1 ), // this is the parameter we add
		$wp_query->query     // this is the base request array of the current page
		это массив базового запроса текущей страницы
	)
);

This example is essentially the same as example 3 (using the global variable $query_string), but uses array to set query parameters.

Filters/Hooks notes

To modify the SQL query, you can use the following filters:

Note: if the parameter suppress_filters = true is specified, none of these filters will work - they will be skipped.

Filters triggered before setting the pagination query:

Filters triggered after setting the pagination query:

  • posts_where_paged - modifies the SQL 'WHERE' during pagination
  • posts_groupby - modifies the SQL 'GROUP BY'
  • posts_join_paged - modifies the SQL 'JOIN' during pagination
  • posts_orderby - modifies the SQL 'ORDER BY'
  • posts_distinct - modifies the SQL 'DISTINCTROW'
  • post_limits - modifies the SQL 'LIMIT'
  • posts_fields - modifies the retrieved table fields
  • posts_clauses - allows modifying all parts simultaneously, passing an array:

    Array (
    	[where]    =>  AND 1 = 1 AND wp_posts.post_type = 'post' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private')
    	[groupby]  => wp_posts.ID
    	[join]     => INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id )
    	[orderby]  => wp_posts.post_date DESC
    	[distinct] =>
    	[fields]   => wp_posts.*
    	[limits]   => LIMIT 0, 10
    )

Properties and Methods

WP_Query properties

$query
Stores the query string.
$query_vars
Associative array with query data.
$tax_query
A query for a taxonomy. Object that is passed to get_tax_sql().
$meta_query
Request parameters for custom fields.
$date_query
Request parameters for dates.
$queried_object
Used in queries such as category, author, post or page. Contains information about category, author, post, or page.
$queried_object_id
If the request relates to a category, author, post or page, it contains the corresponding ID.
$request
Full SQL query that was built on the basis of the passed query parameters.
$posts
Filled with posts data obtained from Database.
$post_count
The number of posts displayed on a page.
$current_post
Available during the loop. The index of the current post that is displayed.
$in_the_loop
Logical variable. Determines whether the loop is started and the called object is in the loop.
$post
Available during the loop. Post which is displayed at the moment.
$comments
List of comments for the current post.
$comment_count
Number of comments for the current post.
$current_comment
The current comment in the comment loop.
$comment
ID of the current comment.
$found_posts
Number of all posts in the query.
$max_num_pages
Count of pagination pages: $found_posts / $posts_per_page
$is_single, $is_page, $is_archive, $is_preview, $is_date, $is_year, $is_month, $is_time, $is_author, $is_category, $is_tag, $is_tax, $is_search, $is_feed, $is_comment_feed, $is_trackback, $is_home, $is_404, $is_comments_popup, $is_admin, $is_attachment, $is_singular, $is_robots, $is_posts_page, $is_paged
Boolean value. Determine which page type the current request belongs to.
$stopwords
Cached list of stopwords for search query.

WP_Query Class methods

  1. public __call( $name, $arguments )
  2. public __construct( $query = '' )
  3. public __get( $name )
  4. public __isset( $name )
  5. public fill_query_vars( $query_vars )
  6. protected generate_cache_key( array $args, $sql )
  7. public generate_postdata( $post )
  8. public get( $query_var, $default_value = '' )
  9. public get_posts()
  10. public get_queried_object()
  11. public get_queried_object_id()
  12. protected get_search_stopwords()
  13. public have_comments()
  14. public have_posts()
  15. public init()
  16. private init_query_flags()
  17. public is_404()
  18. public is_archive()
  19. public is_attachment( $attachment = '' )
  20. public is_author( $author = '' )
  21. public is_category( $category = '' )
  22. public is_comment_feed()
  23. public is_comments_popup()
  24. public is_date()
  25. public is_day()
  26. public is_embed()
  27. public is_favicon()
  28. public is_feed( $feeds = '' )
  29. public is_front_page()
  30. public is_home()
  31. public is_main_query()
  32. public is_month()
  33. public is_page( $page = '' )
  34. public is_paged()
  35. public is_post_type_archive( $post_types = '' )
  36. public is_preview()
  37. public is_privacy_policy()
  38. public is_robots()
  39. public is_search()
  40. public is_single( $post = '' )
  41. public is_singular( $post_types = '' )
  42. public is_tag( $tag = '' )
  43. public is_tax( $taxonomy = '', $term = '' )
  44. public is_time()
  45. public is_trackback()
  46. public is_year()
  47. public lazyload_comment_meta( $check, $comment_id )
  48. public lazyload_term_meta( $check, $term_id )
  49. public next_comment()
  50. public next_post()
  51. protected parse_order( $order )
  52. protected parse_orderby( $orderby )
  53. public parse_query( $query = '' )
  54. public parse_query_vars()
  55. protected parse_search( &$q )
  56. protected parse_search_order( &$q )
  57. protected parse_search_terms( $terms )
  58. public parse_tax_query( &$q )
  59. public query( $query )
  60. public reset_postdata()
  61. public rewind_comments()
  62. public rewind_posts()
  63. public set( $query_var, $value )
  64. public set_404()
  65. private set_found_posts( $q, $limits )
  66. public setup_postdata( $post )
  67. public the_comment()
  68. public the_post()

Changelog

Since 1.5.0 Introduced.
Since 4.5.0 Removed the $comments_popup property.

WP_Query{} code WP 6.4.3

The code is too large. See it here: wp-includes/class-wp-query.php
2 comments
    Log In