diff --git a/README.md b/README.md
index 642e20dcd0..26a356e2c9 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,10 @@ ElasticPress [![Build Status](https://travis-ci.org/10up/ElasticPress.svg?branch
Integrate [Elasticsearch](http://www.elasticsearch.org/) with [WordPress](http://wordpress.org/).
+**Please note:** the master branch is the stable branch
+
+**Latest stable release:** [1.4](https://github.com/10up/ElasticPress/releases/tag/v1.4)
+
## Background
Let's face it, WordPress search is rudimentary at best. Poor performance, inflexible and rigid matching algorithms (which means no comprehension of 'close' queries), the inability to search metadata and taxonomy information, no way to determine categories of your results and most importantly the overall relevancy of results is poor.
@@ -43,6 +47,8 @@ First, make sure you have Elasticsearch and WP-CLI configured properly.
define( 'EP_HOST', 'http://192.168.50.4:9200' );
```
+**Note:** The URL for `EP_HOST` *must* begin with a protocol specifier (`http` or `https`). URLs without a protocol prefix will not be parsed correctly and will cause ElasticPress to error out.
+
The proceeding sets depend on whether you are configuring for single site or multi-site with cross-site search capabilities.
### Single Site
@@ -99,7 +105,120 @@ After running an index, ElasticPress integrates with `WP_Query` if and only if t
) );
```
- ```tax_query``` accepts an array of arrays where each inner array *only* supports ```taxonomy``` (string) and ```terms``` (string|array) parameters. ```terms``` is a slug, either in string or array form.
+ ```tax_query``` accepts an array of arrays where each inner array *only* supports ```taxonomy``` (string) and
+ ```terms``` (string|array) parameters. ```terms``` is a slug, either in string or array form.
+
+* The following shorthand parameters can be used for querying posts by specific dates:
+
+ * ```year``` (int) - 4 digit year (e.g. 2011).
+ * ```month``` or ```monthnum``` (int) - Month number (from 1 to 12).
+ * ```week``` (int) - Week of the year (from 0 to 53).
+ * ```day``` (int) - Day of the month (from 1 to 31).
+ * ```dayofyear``` (int) - Day of the month (from 1 to 365 or 366 for leap year).
+ * ```hour``` (int) - Hour (from 0 to 23).
+ * ```minute``` (int) - Minute (from 0 to 59).
+ * ```second``` (int) - Second (0 to 59).
+ * ```dayofweek``` (int|array) - Weekday number, when week starts at Sunday (1 to 7).
+ * ```dayofweek_iso``` (int|array) - Weekday number, when week starts at Monday (1 to 7).
+
+ This is a simple example which will return posts which are created on January 1st of 2012 from all sites:
+
+ ```php
+ new WP_Query( array(
+ 's' => 'search phrase',
+ 'sites' => 'all',
+ 'year' => 2012,
+ 'monthnum' => 1,
+ 'day' => 1,
+ ) );
+ ```
+
+* ```date_query``` (*array*)
+
+ ```date_query``` accepts an array of keys and values (array|string|int) to find posts created on
+ specific dates/times as well as an array of arrays with keys and values (array|string|int|boolean)
+ containing the following parameters ```after```, ```before```, ```inclusive```, ```compare```, ```column```, and
+ ```relation```. ```column``` is used to query specific columns from the ```wp_posts``` table. This will return posts
+ which are created after January 1st 2012 and January 3rd 2012 8AM GMT:
+
+ ```php
+ new WP_Query( array(
+ 's' => 'search phrase',
+ 'date_query' => array(
+ array(
+ 'column' => 'post_date',
+ 'after' => 'January 1st 2012',
+ ),
+ array(
+ 'column' => 'post_date_gmt',
+ 'after' => 'January 3rd 2012 8AM',
+ ),
+ ),
+ ) );
+ ```
+
+ Currently only the ```AND``` value is supported for the ```relation``` parameter.
+
+ ```inclusive``` is used on after/before options to determine whether exact value should be matched or not. If inclusive is used
+ and you pass in sting without specific time, it will be converted to 00:00:00 on that date. In this case, even if
+ inclusive was set to true, the date would not be included in the query. If you want to include that specific date,
+ you need to pass the time as well. (e.g. 'before' => '2012-01-03 23:59:59')
+
+ The example will return all posts which are created on January 5th 2012 after 10:00PM and 11:00PM inclusively,
+ because the time is specified:
+
+ ```php
+ new WP_Query( array(
+ 's' => 'search phrase',
+ 'date_query' => array(
+ array(
+ 'column' => 'post_date',
+ 'before' => 'January 5th 2012 11:00PM',
+ ),
+ array(
+ 'column' => 'post_date',
+ 'after' => 'January 5th 2012 10:00PM',
+ ),
+ 'inclusive' => true,
+ ),
+ ) );
+ ```
+
+ ```compare``` supports the following options:
+
+ * ```=``` - Posts will be returned that are created on a specified date.
+ * ```!=``` - Posts will be returned that are not created on a specified date.
+ * ```>``` - Posts will be returned that are created after a specified date.
+ * ```>=``` - Posts will be returned that are created on a specified date or after.
+ * ```<``` - Posts will be returned that are created before a specified date.
+ * ```<=``` - Posts will be returned that are created on a specified date or before that.
+ * ```BETWEEN``` - Posts will be returned that are created between a specified range.
+ * ```NOT BETWEEN``` - Posts will be returned that are created not in a specified range.
+ * ```IN``` - Posts will be returned that are created on any of the specified dates.
+ * ```NOT IN``` - Posts will be returned that are not created on any of the specified dates.
+
+ ```compare``` can be combined with shorthand parameters as well as with ```after``` and ```before```. This example
+ will return all posts which are created during Monday to Friday, between 9AM to 5PM:
+
+ ```php
+ new WP_Query( array(
+ 's' => 'search phrase',
+ 'date_query' => array(
+ array(
+ 'hour' => 9,
+ 'compare' => '>=',
+ ),
+ array(
+ 'hour' => 17,
+ 'compare' => '<=',
+ ),
+ array(
+ 'dayofweek' => array( 2, 6 ),
+ 'compare' => 'BETWEEN',
+ ),
+ ),
+ ) );
+ ```
* ```meta_query``` (*array*)
@@ -122,6 +241,10 @@ After running an index, ElasticPress integrates with `WP_Query` if and only if t
* ```=``` - Posts will be returned that have a post meta key corresponding to ```key``` and a value that equals the value passed to ```value```.
* ```!=``` - Posts will be returned that have a post meta key corresponding to ```key``` and a value that does NOT equal the value passed to ```value```.
+ * ```>``` - Posts will be returned that have a post meta key corresponding to ```key``` and a value that is greater than the value passed to ```value```.
+ * ```>=``` - Posts will be returned that have a post meta key corresponding to ```key``` and a value that is greater than or equal to the value passed to ```value```.
+ * ```<``` - Posts will be returned that have a post meta key corresponding to ```key``` and a value that is less than the value passed to ```value```.
+ * ```<=``` - Posts will be returned that have a post meta key corresponding to ```key``` and a value that is less than or equal to the value passed to ```value```.
* ```EXISTS``` - Posts will be returned that have a post meta key corresponding to ```key```.
* ```NOT EXISTS``` - Posts will be returned that do not have a post meta key corresponding to ```key```.
diff --git a/bin/install-wp-tests.sh b/bin/install-wp-tests.sh
index ed4b862fe0..f749512663 100644
--- a/bin/install-wp-tests.sh
+++ b/bin/install-wp-tests.sh
@@ -25,7 +25,7 @@ install_wp() {
local ARCHIVE_NAME="wordpress-$WP_VERSION"
fi
- wget -nv -O /tmp/wordpress.tar.gz http://wordpress.org/${ARCHIVE_NAME}.tar.gz --no-check-certificate
+ wget -nv -O /tmp/wordpress.tar.gz https://wordpress.org/${ARCHIVE_NAME}.tar.gz --no-check-certificate
tar --strip-components=1 -zxmf /tmp/wordpress.tar.gz -C $WP_CORE_DIR
wget -nv -O $WP_CORE_DIR/wp-content/db.php https://raw.github.com/markoheijnen/wp-mysqli/master/db.php --no-check-certificate
@@ -42,9 +42,10 @@ install_test_suite() {
# set up testing suite
mkdir -p $WP_TESTS_DIR
cd $WP_TESTS_DIR
- svn co --quiet http://develop.svn.wordpress.org/trunk/tests/phpunit/includes/
+ svn co --quiet https://develop.svn.wordpress.org/trunk/tests/phpunit/includes/
+
+ wget -nv -O wp-tests-config.php https://develop.svn.wordpress.org/trunk/wp-tests-config-sample.php --no-check-certificate
- wget -nv -O wp-tests-config.php http://develop.svn.wordpress.org/trunk/wp-tests-config-sample.php --no-check-certificate
sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR':" wp-tests-config.php
sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" wp-tests-config.php
sed $ioption "s/yourusernamehere/$DB_USER/" wp-tests-config.php
diff --git a/bin/wp-cli.php b/bin/wp-cli.php
index 414bfa623c..35ff88e7f7 100644
--- a/bin/wp-cli.php
+++ b/bin/wp-cli.php
@@ -443,6 +443,8 @@ private function send_bulk_errors() {
WP_CLI::log( $email_text );
}
}
+ // clear failed posts after sending emails
+ $this->failed_posts = array();
}
}
@@ -479,16 +481,20 @@ public function stats() {
if ( is_wp_error( $request ) ) {
WP_CLI::error( implode( "\n", $request->get_error_messages() ) );
}
- $body = json_decode( wp_remote_retrieve_body( $request ), true );
- $current_index = ep_get_index_name();
-
- if ( isset( $body['indices'][$current_index] ) ) {
- WP_CLI::log( '====== Stats for: ' . $current_index . " ======" );
- WP_CLI::log( 'Documents: ' . $body['indices'][$current_index]['total']['docs']['count'] );
- WP_CLI::log( 'Index Size: ' . size_format( $body['indices'][$current_index]['total']['store']['size_in_bytes'], 2 ) );
- WP_CLI::log( '====== End Stats ======' );
- } else {
- WP_CLI::warning( $current_index . ' is not currently indexed.' );
+ $body = json_decode( wp_remote_retrieve_body( $request ), true );
+ $sites = ( is_multisite() ) ? ep_get_sites() : array( 'blog_id' => get_current_blog_id() );
+
+ foreach ( $sites as $site ) {
+ $current_index = ep_get_index_name( $site['blog_id'] );
+
+ if (isset( $body['indices'][$current_index] ) ) {
+ WP_CLI::log( '====== Stats for: ' . $current_index . " ======" );
+ WP_CLI::log( 'Documents: ' . $body['indices'][$current_index]['total']['docs']['count'] );
+ WP_CLI::log( 'Index Size: ' . size_format($body['indices'][$current_index]['total']['store']['size_in_bytes'], 2 ) );
+ WP_CLI::log( '====== End Stats ======' );
+ } else {
+ WP_CLI::warning( $current_index . ' is not currently indexed.' );
+ }
}
}
diff --git a/classes/class-ep-api.php b/classes/class-ep-api.php
index 42108d90aa..71e84a2b79 100644
--- a/classes/class-ep-api.php
+++ b/classes/class-ep-api.php
@@ -38,7 +38,9 @@ public function index_post( $post ) {
$url = $index_url . '/post/' . $post['post_id'];
- $request = wp_remote_request( $url, array( 'body' => json_encode( $post ), 'method' => 'PUT' ) );
+ $request_args = array( 'body' => json_encode( $post ), 'method' => 'PUT', 'timeout' => 15, 'headers' => $this->format_request_headers() );
+
+ $request = wp_remote_request( $url, apply_filters( 'ep_index_post_request_args', $request_args, $post ) );
if ( ! is_wp_error( $request ) ) {
$response_body = wp_remote_retrieve_body( $request );
@@ -67,7 +69,9 @@ public function parse_site_id( $index_name ) {
* @return bool
*/
public function refresh_index() {
- $request = wp_remote_request( ep_get_index_url() . '/_refresh', array( 'method' => 'POST' ) );
+ $request_args = array( 'method' => 'POST', 'headers' => $this->format_request_headers() );
+
+ $request = wp_remote_request( ep_get_index_url() . '/_refresh', apply_filters( 'ep_refresh_index_request_args', $request_args ) );
if ( ! is_wp_error( $request ) ) {
if ( isset( $request['response']['code'] ) && 200 === $request['response']['code'] ) {
@@ -105,7 +109,9 @@ public function search( $args, $scope = 'current' ) {
$url = $index_url . '/post/_search';
- $request = wp_remote_request( $url, array( 'body' => json_encode( $args ), 'method' => 'POST' ) );
+ $request_args = array( 'body' => json_encode( $args ), 'method' => 'POST', 'headers' => $this->format_request_headers() );
+
+ $request = wp_remote_request( $url, apply_filters( 'ep_search_request_args', $request_args, $args, $scope ) );
if ( ! is_wp_error( $request ) ) {
@@ -178,7 +184,9 @@ public function delete_post( $post_id ) {
$url = $index_url . '/post/' . $post_id;
- $request = wp_remote_request( $url, array( 'method' => 'DELETE' ) );
+ $request_args = array( 'method' => 'DELETE', 'timeout' => 15, 'headers' => $this->format_request_headers() );
+
+ $request = wp_remote_request( $url, apply_filters( 'ep_delete_post_request_args', $request_args, $post_id ) );
if ( ! is_wp_error( $request ) ) {
$response_body = wp_remote_retrieve_body( $request );
@@ -193,6 +201,22 @@ public function delete_post( $post_id ) {
return false;
}
+ /**
+ * Add appropriate request headers
+ *
+ * @since 1.4
+ * @return array
+ */
+ public function format_request_headers() {
+ $headers = array();
+
+ if ( defined( 'EP_API_KEY' ) && EP_API_KEY ) {
+ $headers['X-ElasticPress-API-Key'] = EP_API_KEY;
+ }
+
+ return $headers;
+ }
+
/**
* Get a post from the index
*
@@ -205,7 +229,9 @@ public function get_post( $post_id ) {
$url = $index_url . '/post/' . $post_id;
- $request = wp_remote_request( $url, array( 'method' => 'GET' ) );
+ $request_args = array( 'method' => 'GET', 'headers' => $this->format_request_headers() );
+
+ $request = wp_remote_request( $url, apply_filters( 'ep_get_post_request_args', $request_args, $post_id ) );
if ( ! is_wp_error( $request ) ) {
$response_body = wp_remote_retrieve_body( $request );
@@ -229,7 +255,9 @@ public function get_post( $post_id ) {
public function delete_network_alias() {
$url = untrailingslashit( EP_HOST ) . '/*/_alias/' . ep_get_network_alias();
- $request = wp_remote_request( $url, array( 'method' => 'DELETE' ) );
+ $request_args = array( 'method' => 'DELETE', 'headers' => $this->format_request_headers() );
+
+ $request = wp_remote_request( $url, apply_filters( 'ep_delete_network_alias_request_args', $request_args ) );
if ( ! is_wp_error( $request ) && ( 200 >= wp_remote_retrieve_response_code( $request ) && 300 > wp_remote_retrieve_response_code( $request ) ) ) {
$response_body = wp_remote_retrieve_body( $request );
@@ -263,7 +291,9 @@ public function create_network_alias( $indexes ) {
);
}
- $request = wp_remote_request( $url, array( 'body' => json_encode( $args ), 'method' => 'POST' ) );
+ $request_args = array( 'body' => json_encode( $args ), 'method' => 'POST', 'headers' => $this->format_request_headers() );
+
+ $request = wp_remote_request( $url, apply_filters( 'ep_create_network_alias_request_args', $request_args, $args, $indexes ) );
if ( ! is_wp_error( $request ) && ( 200 >= wp_remote_retrieve_response_code( $request ) && 300 > wp_remote_retrieve_response_code( $request ) ) ) {
$response_body = wp_remote_retrieve_body( $request );
@@ -281,239 +311,38 @@ public function create_network_alias( $indexes ) {
* @return array|bool|mixed
*/
public function put_mapping() {
- $mapping = array(
- 'settings' => array(
- 'index' => array(
- 'number_of_shards' => (int) apply_filters( 'ep_default_index_number_of_shards', 5 ), // Default within Elasticsearch
- 'number_of_replicas' => (int) apply_filters( 'ep_default_index_number_of_replicas', 1 ), // Default within Elasticsearch
- ),
- 'analysis' => array(
- 'analyzer' => array(
- 'default' => array(
- 'tokenizer' => 'standard',
- 'filter' => array( 'standard', 'ewp_word_delimiter', 'lowercase', 'stop', 'ewp_snowball' ),
- 'language' => apply_filters( 'ep_analyzer_language', 'English' ),
- ),
- 'shingle_analyzer' => array(
- 'type' => 'custom',
- 'tokenizer' => 'standard',
- 'filter' => array( 'lowercase', 'shingle_filter' )
- ),
- ),
- 'filter' => array(
- 'shingle_filter' => array(
- 'type' => 'shingle',
- 'min_shingle_size' => 2,
- 'max_shingle_size' => 5
- ),
- 'ewp_word_delimiter' => array(
- 'type' => 'word_delimiter',
- 'preserve_original' => true
- ),
- 'ewp_snowball' => array(
- 'type' => 'snowball',
- 'language' => apply_filters( 'ep_analyzer_language', 'English' ),
- ),
- 'edge_ngram' => array(
- 'side' => 'front',
- 'max_gram' => 10,
- 'min_gram' => 3,
- 'type' => 'edgeNGram'
- )
- )
- )
- ),
- 'mappings' => array(
- 'post' => array(
- "date_detection" => false,
- "dynamic_templates" => array(
- array(
- "template_meta" => array(
- "path_match" => "post_meta.*",
- "mapping" => array(
- "type" => "multi_field",
- "path" => "full",
- "fields" => array(
- "{name}" => array(
- "type" => "string",
- "index" => "analyzed"
- ),
- "raw" => array(
- "type" => "string",
- "index" => "not_analyzed",
- 'include_in_all' => false
- )
- )
- )
- )
- ),
- array(
- "template_terms" => array(
- "path_match" => "terms.*",
- "mapping" => array(
- "type" => "object",
- "path" => "full",
- "properties" => array(
- "name" => array(
- "type" => "string"
- ),
- "term_id" => array(
- "type" => "long"
- ),
- "parent" => array(
- "type" => "long"
- ),
- "slug" => array(
- "type" => "string",
- "index" => "not_analyzed"
- )
- )
- )
- )
- ),
- array(
- "term_suggest" => array(
- "path_match" => "term_suggest_*",
- "mapping" => array(
- "type" => "completion",
- "analyzer" => "default",
- )
- )
- )
- ),
- "_all" => array(
- "analyzer" => "simple"
- ),
- 'properties' => array(
- 'post_id' => array(
- 'type' => 'long',
- 'index' => 'not_analyzed',
- 'include_in_all' => false
- ),
- 'post_author' => array(
- 'type' => 'object',
- 'path' => 'full',
- 'fields' => array(
- 'display_name' => array(
- 'type' => 'string',
- 'analyzer' => 'standard',
- ),
- 'login' => array(
- 'type' => 'string',
- 'analyzer' => 'standard',
- ),
- 'id' => array(
- 'type' => 'long',
- 'index' => 'not_analyzed'
- ),
- 'raw' => array(
- 'type' => 'string',
- 'index' => 'not_analyzed',
- 'include_in_all' => false
- )
- )
- ),
- 'post_date' => array(
- 'type' => 'date',
- 'format' => 'YYYY-MM-dd HH:mm:ss',
- 'include_in_all' => false
- ),
- 'post_date_gmt' => array(
- 'type' => 'date',
- 'format' => 'YYYY-MM-dd HH:mm:ss',
- 'include_in_all' => false
- ),
- 'post_title' => array(
- 'type' => 'multi_field',
- 'fields' => array(
- 'post_title' => array(
- 'type' => 'string',
- 'analyzer' => 'standard',
- 'store' => 'yes'
- ),
- 'raw' => array(
- 'type' => 'string',
- 'index' => 'not_analyzed',
- 'include_in_all' => false
- )
- )
- ),
- 'post_excerpt' => array(
- 'type' => 'string'
- ),
- 'post_content' => array(
- 'type' => 'string',
- 'analyzer' => 'default'
- ),
- 'post_status' => array(
- 'type' => 'string',
- 'index' => 'not_analyzed'
- ),
- 'post_name' => array(
- 'type' => 'multi_field',
- 'fields' => array(
- 'post_name' => array(
- 'type' => 'string'
- ),
- 'raw' => array(
- 'type' => 'string',
- 'index' => 'not_analyzed',
- 'include_in_all' => false
- )
- )
- ),
- 'post_modified' => array(
- 'type' => 'date',
- 'format' => 'YYYY-MM-dd HH:mm:ss',
- 'include_in_all' => false
- ),
- 'post_modified_gmt' => array(
- 'type' => 'date',
- 'format' => 'YYYY-MM-dd HH:mm:ss',
- 'include_in_all' => false
- ),
- 'post_parent' => array(
- 'type' => 'long',
- 'index' => 'not_analyzed',
- 'include_in_all' => false
- ),
- 'post_type' => array(
- 'type' => 'multi_field',
- 'fields' => array(
- 'post_type' => array(
- 'type' => 'string'
- ),
- 'raw' => array(
- 'type' => 'string',
- 'index' => 'not_analyzed',
- 'include_in_all' => false
- )
- )
- ),
- 'post_mime_type' => array(
- 'type' => 'string',
- 'index' => 'not_analyzed',
- 'include_in_all' => false
- ),
- 'permalink' => array(
- 'type' => 'string'
- ),
- 'terms' => array(
- "type" => "object"
- ),
- 'post_meta' => array(
- 'type' => 'object'
- )
- )
- )
- )
- );
+ $mapping = require( apply_filters( 'ep_config_mapping_file', dirname( __FILE__ ) . '/../includes/mappings.php' ) );
+
+ /**
+ * We are removing shard/replica defaults but need to maintain the filters
+ * for backwards compat.
+ *
+ * @since 1.4
+ */
+ global $wp_filter;
+ if ( ! empty( $wp_filter['ep_default_index_number_of_shards'] ) ) {
+ if ( empty( $mapping['settings']['index'] ) ) {
+ $mapping['settings']['index'] = array();
+ }
+
+ $mapping['settings']['index']['number_of_shards'] = (int) apply_filters( 'ep_default_index_number_of_shards', 5 ); // Default within Elasticsearch
+ }
+
+ if ( ! empty( $wp_filter['ep_default_index_number_of_replicas'] ) ) {
+ if ( empty( $mapping['settings']['index'] ) ) {
+ $mapping['settings']['index'] = array();
+ }
+
+ $mapping['settings']['index']['number_of_replicas'] = (int) apply_filters( 'ep_default_index_number_of_replicas', 1 );
+ }
$mapping = apply_filters( 'ep_config_mapping', $mapping );
$index_url = ep_get_index_url();
- $request = wp_remote_request( $index_url, array( 'body' => json_encode( $mapping ), 'method' => 'PUT' ) );
+ $request_args = array( 'body' => json_encode( $mapping ), 'method' => 'PUT', 'headers' => $this->format_request_headers() );
+
+ $request = wp_remote_request( $index_url, apply_filters( 'ep_put_mapping_request_args', $request_args ) );
$request = apply_filters( 'ep_config_mapping_request', $request, $index_url, $mapping );
@@ -595,14 +424,43 @@ public function prepare_post( $post_id ) {
'permalink' => get_permalink( $post_id ),
'terms' => $this->prepare_terms( $post ),
'post_meta' => $this->prepare_meta( $post ),
+ 'date_terms' => $this->prepare_date_terms( $post_date ),
//'site_id' => get_current_blog_id(),
);
+ /**
+ * This filter is named poorly but has to stay to keep backwards compat
+ */
$post_args = apply_filters( 'ep_post_sync_args', $post_args, $post_id );
-
return $post_args;
}
+ /**
+ * Prepare date terms to send to ES.
+ *
+ * @param string $timestamp
+ *
+ * @since 0.1.4
+ * @return array
+ */
+ private function prepare_date_terms( $post_date_gmt ) {
+ $timestamp = strtotime( $post_date_gmt );
+ $date_terms = array(
+ 'year' => (int) date( "Y", $timestamp),
+ 'month' => (int) date( "m", $timestamp),
+ 'week' => (int) date( "W", $timestamp),
+ 'dayofyear' => (int) date( "z", $timestamp),
+ 'day' => (int) date( "d", $timestamp),
+ 'dayofweek' => (int) date( "w", $timestamp),
+ 'dayofweek_iso' => (int) date( "N", $timestamp),
+ 'hour' => (int) date( "H", $timestamp),
+ 'minute' => (int) date( "i", $timestamp),
+ 'second' => (int) date( "s", $timestamp),
+ 'm' => (int) (date( "Y", $timestamp) . date( "m", $timestamp)), // yearmonth
+ );
+ return $date_terms;
+ }
+
/**
* Prepare terms to send to ES.
*
@@ -676,15 +534,20 @@ public function prepare_meta( $post ) {
}
/**
- * Delete the current index
+ * Delete the current index or delete the index passed by name
+ *
+ * @param string $index_name
*
* @since 0.9.0
* @return array|bool
*/
- public function delete_index( ) {
- $index_url = ep_get_index_url();
+ public function delete_index( $index_name = null ) {
+
+ $index_url = ep_get_index_url( $index_name );
- $request = wp_remote_request( $index_url, array( 'method' => 'DELETE' ) );
+ $request_args = array( 'method' => 'DELETE', 'headers' => $this->format_request_headers() );
+
+ $request = wp_remote_request( $index_url, apply_filters( 'ep_delete_index_request_args', $request_args ) );
// 200 means the delete was successful
// 404 means the index was non-existent, but we should still pass this through as we will occasionally want to delete an already deleted index
@@ -697,6 +560,36 @@ public function delete_index( ) {
return false;
}
+ /**
+ * Checks if index exists by index name, returns true or false
+ *
+ * @param null $index_name
+ *
+ * @return bool
+ */
+ public function index_exists( $index_name = null ) {
+ $index_url = ep_get_index_url( $index_name );
+
+ $request_args = array( 'headers' => $this->format_request_headers() );
+
+ $request = wp_remote_head( $index_url, apply_filters( 'ep_index_exists_request_args', $request_args, $index_name ) );
+
+ // 200 means the index exists
+ // 404 means the index was non-existent
+ if ( ! is_wp_error( $request ) && ( 200 === wp_remote_retrieve_response_code( $request ) || 404 === wp_remote_retrieve_response_code( $request ) ) ) {
+
+ if ( 404 === wp_remote_retrieve_response_code( $request ) ) {
+ return false;
+ }
+
+ if ( 200 === wp_remote_retrieve_response_code( $request ) ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
/**
* Format WP query args for ES
*
@@ -819,6 +712,33 @@ public function format_args( $args ) {
$use_filters = true;
}
+ /**
+ * Simple date params support
+ *
+ * @since 1.3
+ */
+ if ( $date_filter = EP_WP_Date_Query::simple_es_date_filter( $args ) ) {
+ $filter['and'][] = $date_filter;
+ $use_filters = true;
+ }
+
+ /**
+ * 'date_query' arg support.
+ *
+ */
+ if ( ! empty( $args['date_query'] ) ) {
+
+ $date_query = new EP_WP_Date_Query( $args['date_query'] );
+
+ $date_filter = $date_query->get_es_filter();
+
+ if( array_key_exists('and', $date_filter ) ) {
+ $filter['and'][] = $date_filter['and'];
+ $use_filters = true;
+ }
+
+ }
+
/**
* 'meta_query' arg support.
*
@@ -884,6 +804,78 @@ public function format_args( $args ) {
),
);
+ break;
+ case '>=':
+ if ( isset( $single_meta_query['value'] ) ) {
+ $terms_obj = array(
+ 'bool' => array(
+ 'must' => array(
+ array(
+ 'range' => array(
+ 'post_meta.' . $single_meta_query['key'] . '.raw' => array(
+ "gte" => $single_meta_query['value'],
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ break;
+ case '<=':
+ if ( isset( $single_meta_query['value'] ) ) {
+ $terms_obj = array(
+ 'bool' => array(
+ 'must' => array(
+ array(
+ 'range' => array(
+ 'post_meta.' . $single_meta_query['key'] . '.raw' => array(
+ "lte" => $single_meta_query['value'],
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ break;
+ case '>':
+ if ( isset( $single_meta_query['value'] ) ) {
+ $terms_obj = array(
+ 'bool' => array(
+ 'must' => array(
+ array(
+ 'range' => array(
+ 'post_meta.' . $single_meta_query['key'] . '.raw' => array(
+ "gt" => $single_meta_query['value'],
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ break;
+ case '<':
+ if ( isset( $single_meta_query['value'] ) ) {
+ $terms_obj = array(
+ 'bool' => array(
+ 'must' => array(
+ array(
+ 'range' => array(
+ 'post_meta.' . $single_meta_query['key'] . '.raw' => array(
+ "lt" => $single_meta_query['value'],
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
break;
case '=':
default:
@@ -1059,7 +1051,6 @@ public function format_args( $args ) {
$formatted_args['aggs'][ $agg_name ] = $args['aggs'];
}
}
-
return apply_filters( 'ep_formatted_args', $formatted_args, $args );
}
@@ -1082,7 +1073,10 @@ public function get_sites() {
public function bulk_index_posts( $body ) {
// create the url with index name and type so that we don't have to repeat it over and over in the request (thereby reducing the request size)
$url = trailingslashit( EP_HOST ) . trailingslashit( ep_get_index_name() ) . 'post/_bulk';
- $request = wp_remote_request( $url, array( 'method' => 'POST', 'body' => $body ) );
+
+ $request_args = array( 'method' => 'POST', 'body' => $body, 'timeout' => 30, 'headers' => $this->format_request_headers() );
+
+ $request = wp_remote_request( $url, apply_filters( 'ep_bulk_index_posts_request_args', $request_args, $body ) );
return is_wp_error( $request ) ? $request : json_decode( wp_remote_retrieve_body( $request ), true );
}
@@ -1230,7 +1224,9 @@ public function elasticsearch_alive() {
$url = EP_HOST;
- $request = wp_remote_request( $url );
+ $request_args = array( 'headers' => $this->format_request_headers() );
+
+ $request = wp_remote_request( $url, apply_filters( 'ep_es_alive_request_args', $request_args ) );
if ( ! is_wp_error( $request ) ) {
if ( isset( $request['response']['code'] ) && 200 === $request['response']['code'] ) {
@@ -1268,8 +1264,8 @@ function ep_put_mapping() {
return EP_API::factory()->put_mapping();
}
-function ep_delete_index() {
- return EP_API::factory()->delete_index();
+function ep_delete_index( $index_name = null ) {
+ return EP_API::factory()->delete_index( $index_name );
}
function ep_format_args( $args ) {
@@ -1320,6 +1316,6 @@ function ep_elasticsearch_alive() {
return EP_API::factory()->elasticsearch_alive();
}
-function ep_index_exists() {
- return EP_API::factory()->index_exists();
+function ep_index_exists( $index_name = null ) {
+ return EP_API::factory()->index_exists( $index_name );
}
\ No newline at end of file
diff --git a/classes/class-ep-sync-manager.php b/classes/class-ep-sync-manager.php
index 315b4a536a..974f648778 100644
--- a/classes/class-ep-sync-manager.php
+++ b/classes/class-ep-sync-manager.php
@@ -17,6 +17,15 @@ public function __construct() { }
public function setup() {
add_action( 'transition_post_status', array( $this, 'action_sync_on_transition' ), 10, 3 );
add_action( 'delete_post', array( $this, 'action_delete_post' ) );
+ add_action( 'delete_blog', array( $this, 'action_delete_blog_from_index') );
+ add_action( 'archive_blog', array( $this, 'action_delete_blog_from_index') );
+ add_action( 'deactivate_blog', array( $this, 'action_delete_blog_from_index') );
+ }
+
+ public function action_delete_blog_from_index( $blog_id ) {
+ if ( ep_index_exists( ep_get_index_name( $blog_id ) ) && ! apply_filters( 'ep_keep_index', false ) ) {
+ ep_delete_index( ep_get_index_name( $blog_id ) );
+ }
}
/**
@@ -105,7 +114,11 @@ public static function factory() {
*/
public function sync_post( $post_id ) {
- $post_args = apply_filters( 'ep_post_sync_args', ep_prepare_post( $post_id ), $post_id );
+ $post_args = ep_prepare_post( $post_id );
+
+ if ( apply_filters( 'ep_post_sync_kill', false, $post_args, $post_id ) ) {
+ return;
+ }
$response = ep_index_post( $post_args );
diff --git a/classes/class-ep-wp-date-query.php b/classes/class-ep-wp-date-query.php
new file mode 100644
index 0000000000..b80100d03c
--- /dev/null
+++ b/classes/class-ep-wp-date-query.php
@@ -0,0 +1,546 @@
+get_es_filter_for_clauses();
+
+ return $filter;
+ }
+
+ /**
+ * Like WP_Date_Query::get_sql_for_clauses
+ * takes all queries in WP_Date_Query object and gets ES filters for each
+ *
+ * @since 0.1.4
+ * @return array
+ */
+ protected function get_es_filter_for_clauses() {
+ $filter = $this->get_es_filter_for_query( $this->queries );
+
+ return $filter;
+ }
+
+ /**
+ * @param $query array of date query clauses
+ * @param int $depth unused but may be necessary if we do nested date queries
+ *
+ * @since 0.1.4
+ * @return array
+ */
+ protected function get_es_filter_for_query( $query, $depth = 0 ) {
+ $filter_chunks = array(
+ 'filters' => array(),
+ );
+
+ $filter_array = array();
+
+ foreach ( $query as $key => $clause ) {
+ if ( 'relation' === $key ) {
+ $relation = $query['relation'];
+ } else if ( is_array( $clause ) ) {
+
+ // This is a first-order clause.
+ if ( $this->is_first_order_clause( $clause ) ) {
+
+ $clause_filter = $this->get_es_filter_for_clause( $clause, $query );
+
+ $filter_count = count( $clause_filter );
+ if ( ! $filter_count ) {
+ $filter_chunks['filters'][] = '';
+ } else {
+ $filter_chunks['filters'][] = $clause_filter;
+ }
+
+ // This is a subquery, so we recurse.
+ } else {
+ //@todo WP_Date_Query supports nested date queries, revisit if necessary
+ //Removed because this implementation had incorrect results
+ }
+ }
+ }
+
+ //@todo implement OR filter relationships
+ if ( empty( $relation ) ) {
+ $relation = 'AND';
+ }
+
+ if ( 'AND' === $relation ) {
+ $filter_array['and'] = array();
+
+ $range_filters = array();
+ $term_filters = array();
+ foreach ( $filter_chunks['filters'] as $key => $filter ) {
+ $filter_type = key( $filter );
+ if ( 'date_terms' === $filter_type ) {
+ $term_filters[] = $filter['date_terms'];
+ } else if ( 'range_filters' === $filter_type ) {
+ $range_filters[] = $filter['range_filters'];
+ }
+ }
+
+ if ( $range_filters ) {
+ $filter_array['and'] = array(
+ 'bool' => $this->build_es_range_filter( $range_filters ),
+ );
+ }
+
+ if ( $term_filters ) {
+ $filter_array['and'] = array(
+ 'bool' => $this->build_es_date_term_filter( $term_filters ),
+ );
+ }
+ }
+
+ return $filter_array;
+ }
+
+ /**
+ * Takes array of date term filters and groups them into a filter based on
+ * relationship type
+ *
+ * @param array $date_term_filters
+ * @param string $type type of relationship between date term filters (AND, OR)
+ *
+ * @since 0.1.4
+ * @return array
+ */
+ protected function build_es_date_term_filter( $date_term_filters = array(), $type = 'AND' ) {
+ $date_term_filter_array = array(
+ 'must' => array(),
+ 'should' => array(),
+ 'must_not' => array(),
+ );
+
+ if ( 'AND' === $type ) {
+ foreach ( $date_term_filters as $date_term_filter ) {
+ $date_term_filter_array['must'] = ! empty( $date_term_filter['must'] ) ? array_merge( $date_term_filter_array['must'], $date_term_filter['must'] ) : array();
+ $date_term_filter_array['should'] = ! empty( $date_term_filter['should'] ) ? array_merge( $date_term_filter_array['should'], $date_term_filter['should'] ) : array();
+ $date_term_filter_array['must_not'] = ! empty( $date_term_filter['must_not'] ) ? array_merge( $date_term_filter_array['must_not'], $date_term_filter['must_not'] ) : array();
+ }
+ }
+
+ return array_filter( $date_term_filter_array );
+ }
+
+ /**
+ * Takes array of range filters and groups them into a single filter
+ *
+ * @param array $range_filters
+ *
+ * @since 0.1.4
+ * @return array
+ */
+ protected function build_es_range_filter( $range_filters = array() ) {
+ $range_filter_array = array(
+ 'must' => array(),
+ 'should' => array(),
+ 'must_not' => array(),
+ );
+
+ foreach ( $range_filters as $key => $range_filter ) {
+ if ( 'not' === key( $range_filter ) ) {
+ $range_filter_array['must_not'][] = array(
+ 'range' => $range_filter['not']
+ );
+ } else {
+ $range_filter_array['must'][] = array(
+ 'range' => $range_filter
+ );
+ }
+ }
+
+ return array_filter( $range_filter_array );
+ }
+
+ /**
+ * Takes SQL query part, and translates it into an ES filter
+ *
+ * @param $query
+ *
+ * @return array ES filter
+ */
+ protected function get_es_filter_for_clause( $query ) {
+
+ // The sub-parts of a $where part.
+ $filter_parts = array();
+
+ $column = ( ! empty( $query['column'] ) ) ? $query['column'] : $this->column;
+
+ $compare = $this->get_compare( $query );
+
+ $inclusive = ! empty( $query['inclusive'] );
+
+ // Assign greater- and less-than values.
+ $lt = 'lt';
+ $gt = 'gt';
+
+ if ( $inclusive ) {
+ $lt .= 'e';
+ $gt .= 'e';
+ }
+
+
+ // Range queries.
+
+ if ( ! empty( $query['after'] ) ) {
+ $range_filters = array(
+ "{$column}" => array(
+ "{$gt}" => $this->build_mysql_datetime( $query['after'] )
+ )
+ );
+ }
+
+ if ( ! empty( $query['before'] ) ) {
+ $range_filters = empty( $range_filters[ $column ] ) ? array( "{$column}" => array( "{$lt}" => array() ) ) : $range_filters;
+ $range_filters[ $column ][ $lt ] = $this->build_mysql_datetime( $query['before'] );
+ }
+
+ if ( ! empty( $query['after'] ) || ! empty( $query['before'] ) ) {
+ $filter_parts['range_filters'] = $range_filters;
+ }
+
+
+ // Specific value queries.
+
+ $date_parameters = array(
+ 'year' => ! empty( $query['year'] ) ? $query['year'] : false,
+ 'month' => ! empty( $query['month'] ) ? $query['month'] : false,
+ 'week' => ! empty( $query['week'] ) ? $query['week'] : false,
+ 'dayofyear' => ! empty( $query['dayofyear'] ) ? $query['dayofyear'] : false,
+ 'day' => ! empty( $query['day'] ) ? $query['day'] : false,
+ 'dayofweek' => ! empty( $query['dayofweek'] ) ? $query['dayofweek'] : false,
+ 'dayofweek_iso' => ! empty( $query['dayofweek_iso'] ) ? $query['dayofweek_iso'] : false,
+ 'hour' => ! empty( $query['hour'] ) ? $query['hour'] : false,
+ 'minute' => ! empty( $query['minute'] ) ? $query['minute'] : false,
+ 'second' => ! empty( $query['second'] ) ? $query['second'] : false,
+ 'm' => ! empty( $query['m'] ) ? $query['m'] : false, // yearmonth
+ );
+
+ if ( empty( $date_parameters['month'] ) && ! empty( $query['monthnum'] ) ) {
+ $date_parameters['month'] = $query['monthnum'];
+ }
+
+ if ( empty( $date_parameters['week'] ) && ! empty( $query['w'] ) ) {
+ $date_parameters['week'] = $query['w'];
+ }
+
+ foreach ( $date_parameters as $param => $value ) {
+ if ( false === $value ) {
+ unset( $date_parameters[ $param ] );
+ }
+ }
+
+ if ( $date_parameters ) {
+
+ EP_WP_Date_Query::validate_date_values( $date_parameters );
+
+ $date_terms = array(
+ 'must' => array(),
+ 'should' => array(),
+ 'must_not' => array(),
+ );
+
+ foreach ( $date_parameters as $param => $value ) {
+ if ( '=' === $compare ) {
+ $date_terms['must'][]['term']["date_terms.{$param}"] = $value;
+ } else if ( '!=' === $compare ) {
+ $date_terms['must_not'][]['term']["date_terms.{$param}"] = $value;
+ } else if ( 'IN' === $compare ) {
+ foreach ( $value as $in_value ) {
+ $date_terms['should'][]['term']["date_terms.{$param}"] = $in_value;
+ }
+ } else if ( 'NOT IN' === $compare ) {
+ foreach ( $value as $in_value ) {
+ $date_terms['must_not'][]['term']["date_terms.{$param}"] = $in_value;
+ }
+ } else if ( 'BETWEEN' === $compare ) {
+ $range_filter["date_terms.{$param}"] = array();
+ $range_filter["date_terms.{$param}"]['gte'] = $value[0];
+ $range_filter["date_terms.{$param}"]['lte'] = $value[1];
+ $filter_parts['range_filters'] = $range_filter;
+ } else if ( 'NOT BETWEEN' === $compare ) {
+ $range_filter["date_terms.{$param}"] = array();
+ $range_filter["date_terms.{$param}"]['gt'] = $value[0];
+ $range_filter["date_terms.{$param}"]['lt'] = $value[1];
+ $filter_parts['range_filters'] = array( 'not' => $range_filter );
+ } else if ( strpos( $compare, '>' ) !== false ) {
+ $range = ( strpos( $compare, '=' ) !== false ) ? 'gte' : 'gt';
+ $range_filter["date_terms.{$param}"] = array();
+ $range_filter["date_terms.{$param}"][ $range ] = $value;
+ $filter_parts['range_filters'] = $range_filter;
+ } else if ( strpos( $compare, '<' ) !== false ) {
+ $range = ( strpos( $compare, '=' ) !== false ) ? 'lte' : 'lt';
+ $range_filter["date_terms.{$param}"] = array();
+ $range_filter["date_terms.{$param}"][ $range ] = $value;
+ $filter_parts['range_filters'] = $range_filter;
+ }
+ }
+
+ $date_terms = array_filter( $date_terms );
+
+ if ( ! empty( $date_terms ) ) {
+ $filter_parts['date_terms'] = $date_terms;
+ }
+ }
+
+ return $filter_parts;
+ }
+
+ /**
+ * Takes WP_Query args, and returns ES filters for query
+ * Support for older style WP_Query date params
+ *
+ * @param $args
+ *
+ * @return array|bool
+ */
+ static function simple_es_date_filter( $args ) {
+ $date_parameters = array(
+ 'year' => ! empty( $args['year'] ) ? $args['year'] : false,
+ 'month' => ! empty( $args['month'] ) ? $args['month'] : false,
+ 'week' => ! empty( $args['week'] ) ? $args['week'] : false,
+ 'day' => ! empty( $args['day'] ) ? $args['day'] : false,
+ 'hour' => ! empty( $args['hour'] ) ? $args['hour'] : false,
+ 'minute' => ! empty( $args['minute'] ) ? $args['minute'] : false,
+ 'second' => ! empty( $args['second'] ) ? $args['second'] : false,
+ 'm' => ! empty( $args['m'] ) ? $args['m'] : false, // yearmonth
+ );
+
+ if ( ! $date_parameters['month'] && ! empty( $args['monthnum'] ) ) {
+ $date_parameters['month'] = $args['monthnum'];
+ }
+
+ if ( ! $date_parameters['week'] && ! empty( $args['w'] ) ) {
+ $date_parameters['week'] = $args['w'];
+ }
+
+ foreach ( $date_parameters as $param => $value ) {
+ if ( false === $value ) {
+ unset( $date_parameters[ $param ] );
+ }
+ }
+
+ if ( ! $date_parameters ) {
+ return false;
+ }
+
+ $date_terms = array();
+ foreach ( $date_parameters as $param => $value ) {
+ $date_terms[]['term']["date_terms.{$param}"] = $value;
+ }
+
+ return array(
+ 'bool' => array(
+ 'must' => $date_terms,
+ )
+ );
+ }
+
+ /**
+ * Introduced in WP 4.1 added here for backwards compatibility
+ * @var array
+ */
+ public $time_keys = array( 'after', 'before', 'year', 'month', 'monthnum', 'week', 'w', 'dayofyear', 'day', 'dayofweek', 'dayofweek_iso', 'hour', 'minute', 'second' );
+
+ /**
+ * Introduced in WP 4.1 added here for backwards compatibility
+ * @var array
+ */
+ protected function is_first_order_clause( $query ) {
+ $time_keys = array_intersect( $this->time_keys, array_keys( $query ) );
+ return ! empty( $time_keys );
+ }
+
+ /**
+ * Introduced in WP 4.1 added here for backwards compatibility
+ * @var array
+ */
+ public function validate_date_values( $date_query = array() ) {
+ if ( empty( $date_query ) ) {
+ return false;
+ }
+
+ $valid = true;
+
+ /*
+ * Validate 'before' and 'after' up front, then let the
+ * validation routine continue to be sure that all invalid
+ * values generate errors too.
+ */
+ if ( array_key_exists( 'before', $date_query ) && is_array( $date_query['before'] ) ){
+ $valid = $this->validate_date_values( $date_query['before'] );
+ }
+
+ if ( array_key_exists( 'after', $date_query ) && is_array( $date_query['after'] ) ){
+ $valid = $this->validate_date_values( $date_query['after'] );
+ }
+
+ // Array containing all min-max checks.
+ $min_max_checks = array();
+
+ // Days per year.
+ if ( array_key_exists( 'year', $date_query ) ) {
+ /*
+ * If a year exists in the date query, we can use it to get the days.
+ * If multiple years are provided (as in a BETWEEN), use the first one.
+ */
+ if ( is_array( $date_query['year'] ) ) {
+ $_year = reset( $date_query['year'] );
+ } else {
+ $_year = $date_query['year'];
+ }
+
+ $max_days_of_year = date( 'z', mktime( 0, 0, 0, 12, 31, $_year ) ) + 1;
+ } else {
+ // otherwise we use the max of 366 (leap-year)
+ $max_days_of_year = 366;
+ }
+
+ $min_max_checks['dayofyear'] = array(
+ 'min' => 1,
+ 'max' => $max_days_of_year
+ );
+
+ // Days per week.
+ $min_max_checks['dayofweek'] = array(
+ 'min' => 1,
+ 'max' => 7
+ );
+
+ // Days per week.
+ $min_max_checks['dayofweek_iso'] = array(
+ 'min' => 1,
+ 'max' => 7
+ );
+
+ // Months per year.
+ $min_max_checks['month'] = array(
+ 'min' => 1,
+ 'max' => 12
+ );
+
+ // Weeks per year.
+ if ( isset( $_year ) ) {
+ // If we have a specific year, use it to calculate number of weeks.
+ $date = new DateTime();
+ $date->setISODate( $_year, 53 );
+ $week_count = $date->format( "W" ) === "53" ? 53 : 52;
+
+ } else {
+ // Otherwise set the week-count to a maximum of 53.
+ $week_count = 53;
+ }
+
+ $min_max_checks['week'] = array(
+ 'min' => 1,
+ 'max' => $week_count
+ );
+
+ // Days per month.
+ $min_max_checks['day'] = array(
+ 'min' => 1,
+ 'max' => 31
+ );
+
+ // Hours per day.
+ $min_max_checks['hour'] = array(
+ 'min' => 0,
+ 'max' => 23
+ );
+
+ // Minutes per hour.
+ $min_max_checks['minute'] = array(
+ 'min' => 0,
+ 'max' => 59
+ );
+
+ // Seconds per minute.
+ $min_max_checks['second'] = array(
+ 'min' => 0,
+ 'max' => 59
+ );
+
+ // Concatenate and throw a notice for each invalid value.
+ foreach ( $min_max_checks as $key => $check ) {
+ if ( ! array_key_exists( $key, $date_query ) ) {
+ continue;
+ }
+
+ // Throw a notice for each failing value.
+ $is_between = true;
+ foreach ( (array) $date_query[ $key ] as $_value ) {
+ $is_between = $_value >= $check['min'] && $_value <= $check['max'];
+
+ if ( ! is_numeric( $_value ) || ! $is_between ) {
+ $error = sprintf(
+ /* translators: Date query invalid date message: 1: invalid value, 2: type of value, 3: minimum valid value, 4: maximum valid value */
+ __( 'Invalid value %1$s for %2$s. Expected value should be between %3$s and %4$s.' ),
+ '' . esc_html( $_value ) . '
',
+ '' . esc_html( $key ) . '
',
+ '' . esc_html( $check['min'] ) . '
',
+ '' . esc_html( $check['max'] ) . '
'
+ );
+
+ _doing_it_wrong( __CLASS__, $error, '4.1.0' );
+
+ $valid = false;
+ }
+ }
+ }
+
+ // If we already have invalid date messages, don't bother running through checkdate().
+ if ( ! $valid ) {
+ return $valid;
+ }
+
+ $day_month_year_error_msg = '';
+
+ $day_exists = array_key_exists( 'day', $date_query ) && is_numeric( $date_query['day'] );
+ $month_exists = array_key_exists( 'month', $date_query ) && is_numeric( $date_query['month'] );
+ $year_exists = array_key_exists( 'year', $date_query ) && is_numeric( $date_query['year'] );
+
+ if ( $day_exists && $month_exists && $year_exists ) {
+ // 1. Checking day, month, year combination.
+ if ( ! wp_checkdate( $date_query['month'], $date_query['day'], $date_query['year'], sprintf( '%s-%s-%s', $date_query['year'], $date_query['month'], $date_query['day'] ) ) ) {
+ /* translators: 1: year, 2: month, 3: day of month */
+ $day_month_year_error_msg = sprintf(
+ __( 'The following values do not describe a valid date: year %1$s, month %2$s, day %3$s.' ),
+ '' . esc_html( $date_query['year'] ) . '
',
+ '' . esc_html( $date_query['month'] ) . '
',
+ '' . esc_html( $date_query['day'] ) . '
'
+ );
+
+ $valid = false;
+ }
+
+ } else if ( $day_exists && $month_exists ) {
+ /*
+ * 2. checking day, month combination
+ * We use 2012 because, as a leap year, it's the most permissive.
+ */
+ if ( ! wp_checkdate( $date_query['month'], $date_query['day'], 2012, sprintf( '2012-%s-%s', $date_query['month'], $date_query['day'] ) ) ) {
+ /* translators: 1: month, 2: day of month */
+ $day_month_year_error_msg = sprintf(
+ __( 'The following values do not describe a valid date: month %1$s, day %2$s.' ),
+ '' . esc_html( $date_query['month'] ) . '
',
+ '' . esc_html( $date_query['day'] ) . '
'
+ );
+
+ $valid = false;
+ }
+ }
+
+ if ( ! empty( $day_month_year_error_msg ) ) {
+ _doing_it_wrong( __CLASS__, $day_month_year_error_msg, '4.1.0' );
+ }
+
+ return $valid;
+ }
+}
diff --git a/classes/class-ep-wp-query-integration.php b/classes/class-ep-wp-query-integration.php
index 9be55ab4b3..8fbcc909c8 100644
--- a/classes/class-ep-wp-query-integration.php
+++ b/classes/class-ep-wp-query-integration.php
@@ -23,8 +23,23 @@ public function __construct() { }
* @since 0.9
*/
public function setup() {
- // Ensure we aren't on the admin (unless overridden)
- if ( is_admin() && ! apply_filters( 'ep_admin_wp_query_integration', false ) ) {
+
+ /**
+ * By default EP will not integrate on admin or ajax requests. Since admin-ajax.php is
+ * technically an admin request, there is some weird logic here. If we are doing ajax
+ * and ep_ajax_wp_query_integration is filtered true, then we skip the next admin check.
+ */
+ $admin_integration = apply_filters( 'ep_admin_wp_query_integration', false );
+
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
+ if ( ! apply_filters( 'ep_ajax_wp_query_integration', false ) ) {
+ return;
+ } else {
+ $admin_integration = true;
+ }
+ }
+
+ if ( is_admin() && ! $admin_integration ) {
return;
}
@@ -267,12 +282,25 @@ public function filter_posts_request( $request, $query ) {
$post->elasticsearch = true; // Super useful for debugging
// Run through get_post() to add all expected properties (even if they're empty)
- $post = get_post( $post );
+ // do this if mulstisite is disabled
+ // if mulstisite is enabled then we need to proceed differently
+ if( !is_multisite() ) {
+ $post_obj = get_post( $post->ID );
+ // merge with the initial object values
+ $post = (object) array_merge( (array) $post_obj, (array) $post );
+ }
if ( $post ) {
$new_posts[] = $post;
}
}
+
+ // if multisite is enabled get post object for each site
+ if( is_multisite() ) {
+ $new_posts = $this->fill_post_objects( $new_posts );
+ }
+
+
$this->posts_by_query[spl_object_hash( $query )] = $new_posts;
do_action( 'ep_wp_query_search', $new_posts, $search, $query );
@@ -282,6 +310,60 @@ public function filter_posts_request( $request, $query ) {
return "SELECT * FROM $wpdb->posts WHERE 1=0";
}
+ /**
+ * Get post object data in multisite network.
+ * Using post ID is better as get_post function pupulates all possible fields
+ * So passing post ID and then merging with inital post object pupulates all expected fields
+ * for WP_Post object
+ *
+ * @param array $post_list list of found post objects
+ *
+ * @since 1.4
+ * @return array list of WP_Post objects
+ */
+ public function fill_post_objects( $post_list ) {
+ $post_objs = array();
+ $posts_ordered = array();
+ $grouped = array();
+ $order = array();
+
+ // group by site_id to decrease number of switch_to_blogs
+ foreach ( $post_list as $post_data ) {
+ $grouped[$post_data->site_id][] = $post_data;
+ // use this to keep the initial order of posts
+ $order[] = $post_data->ID . '-' . $post_data->site_id;
+ }
+
+ foreach ( $grouped as $site_id => $post_data ) {
+ // switch blog only if needed
+ if ( get_current_blog_id() != $site_id ) {
+ global $switched;
+ switch_to_blog( $site_id );
+ foreach ( $post_data as $single_post_data ) {
+ $post_obj = get_post( $single_post_data->ID );
+ $post_objs[] = (object) array_merge( (array) $post_obj, (array) $single_post_data );
+ }
+ restore_current_blog();
+ } else {
+ foreach ( $post_data as $single_post_data ) {
+ $post_obj = get_post( $single_post_data->ID );
+ $post_objs[] = (object) array_merge( (array) $post_obj, (array) $single_post_data );
+ }
+ }
+ }
+
+ // retrive initial order of posts
+ foreach ( $post_objs as $current_index => $post_obj ) {
+ $index = array_search ( $post_obj->ID . '-' . $post_obj->site_id, $order );
+ $posts_ordered[$index] = $post_objs[$current_index];
+ }
+
+ // sort by indexes/keys
+ ksort( $posts_ordered );
+
+ return $posts_ordered;
+ }
+
/**
* Return a singleton instance of the current class
*
diff --git a/elasticpress.php b/elasticpress.php
index 9d177dfe34..4de7d178c5 100644
--- a/elasticpress.php
+++ b/elasticpress.php
@@ -3,7 +3,7 @@
/**
* Plugin Name: ElasticPress
* Description: Integrate WordPress search with Elasticsearch
- * Version: 1.3.1
+ * Version: 1.4
* Author: Aaron Holbrook, Taylor Lovett, Matt Gross, 10up
* Author URI: http://10up.com
* License: GPLv2 or later
@@ -20,6 +20,7 @@
require_once( 'classes/class-ep-sync-manager.php' );
require_once( 'classes/class-ep-elasticpress.php' );
require_once( 'classes/class-ep-wp-query-integration.php' );
+require_once( 'classes/class-ep-wp-date-query.php' );
/**
* WP CLI Commands
diff --git a/includes/mappings.php b/includes/mappings.php
new file mode 100644
index 0000000000..5dc200e926
--- /dev/null
+++ b/includes/mappings.php
@@ -0,0 +1,264 @@
+ array(
+ 'analysis' => array(
+ 'analyzer' => array(
+ 'default' => array(
+ 'tokenizer' => 'standard',
+ 'filter' => array( 'standard', 'ewp_word_delimiter', 'lowercase', 'stop', 'ewp_snowball' ),
+ 'language' => apply_filters( 'ep_analyzer_language', 'English' ),
+ ),
+ 'shingle_analyzer' => array(
+ 'type' => 'custom',
+ 'tokenizer' => 'standard',
+ 'filter' => array( 'lowercase', 'shingle_filter' )
+ ),
+ ),
+ 'filter' => array(
+ 'shingle_filter' => array(
+ 'type' => 'shingle',
+ 'min_shingle_size' => 2,
+ 'max_shingle_size' => 5
+ ),
+ 'ewp_word_delimiter' => array(
+ 'type' => 'word_delimiter',
+ 'preserve_original' => true
+ ),
+ 'ewp_snowball' => array(
+ 'type' => 'snowball',
+ 'language' => apply_filters( 'ep_analyzer_language', 'English' ),
+ ),
+ 'edge_ngram' => array(
+ 'side' => 'front',
+ 'max_gram' => 10,
+ 'min_gram' => 3,
+ 'type' => 'edgeNGram'
+ )
+ )
+ )
+ ),
+ 'mappings' => array(
+ 'post' => array(
+ "date_detection" => false,
+ "dynamic_templates" => array(
+ array(
+ "template_meta" => array(
+ "path_match" => "post_meta.*",
+ "mapping" => array(
+ "type" => "multi_field",
+ "path" => "full",
+ "fields" => array(
+ "{name}" => array(
+ "type" => "string",
+ "index" => "analyzed"
+ ),
+ "raw" => array(
+ "type" => "string",
+ "index" => "not_analyzed",
+ 'include_in_all' => false
+ )
+ )
+ )
+ )
+ ),
+ array(
+ "template_terms" => array(
+ "path_match" => "terms.*",
+ "mapping" => array(
+ "type" => "object",
+ "path" => "full",
+ "properties" => array(
+ "name" => array(
+ "type" => "string"
+ ),
+ "term_id" => array(
+ "type" => "long"
+ ),
+ "parent" => array(
+ "type" => "long"
+ ),
+ "slug" => array(
+ "type" => "string",
+ "index" => "not_analyzed"
+ )
+ )
+ )
+ )
+ ),
+ array(
+ "term_suggest" => array(
+ "path_match" => "term_suggest_*",
+ "mapping" => array(
+ "type" => "completion",
+ "analyzer" => "default",
+ )
+ )
+ )
+ ),
+ "_all" => array(
+ "analyzer" => "simple"
+ ),
+ 'properties' => array(
+ 'post_id' => array(
+ 'type' => 'long',
+ 'index' => 'not_analyzed',
+ 'include_in_all' => false
+ ),
+ 'post_author' => array(
+ 'type' => 'object',
+ 'path' => 'full',
+ 'fields' => array(
+ 'display_name' => array(
+ 'type' => 'string',
+ 'analyzer' => 'standard',
+ ),
+ 'login' => array(
+ 'type' => 'string',
+ 'analyzer' => 'standard',
+ ),
+ 'id' => array(
+ 'type' => 'long',
+ 'index' => 'not_analyzed'
+ ),
+ 'raw' => array(
+ 'type' => 'string',
+ 'index' => 'not_analyzed',
+ 'include_in_all' => false
+ )
+ )
+ ),
+ 'post_date' => array(
+ 'type' => 'date',
+ 'format' => 'YYYY-MM-dd HH:mm:ss',
+ 'include_in_all' => false
+ ),
+ 'post_date_gmt' => array(
+ 'type' => 'date',
+ 'format' => 'YYYY-MM-dd HH:mm:ss',
+ 'include_in_all' => false
+ ),
+ 'post_title' => array(
+ 'type' => 'multi_field',
+ 'fields' => array(
+ 'post_title' => array(
+ 'type' => 'string',
+ 'analyzer' => 'standard',
+ 'store' => 'yes'
+ ),
+ 'raw' => array(
+ 'type' => 'string',
+ 'index' => 'not_analyzed',
+ 'include_in_all' => false
+ )
+ )
+ ),
+ 'post_excerpt' => array(
+ 'type' => 'string'
+ ),
+ 'post_content' => array(
+ 'type' => 'string',
+ 'analyzer' => 'default'
+ ),
+ 'post_status' => array(
+ 'type' => 'string',
+ 'index' => 'not_analyzed'
+ ),
+ 'post_name' => array(
+ 'type' => 'multi_field',
+ 'fields' => array(
+ 'post_name' => array(
+ 'type' => 'string'
+ ),
+ 'raw' => array(
+ 'type' => 'string',
+ 'index' => 'not_analyzed',
+ 'include_in_all' => false
+ )
+ )
+ ),
+ 'post_modified' => array(
+ 'type' => 'date',
+ 'format' => 'YYYY-MM-dd HH:mm:ss',
+ 'include_in_all' => false
+ ),
+ 'post_modified_gmt' => array(
+ 'type' => 'date',
+ 'format' => 'YYYY-MM-dd HH:mm:ss',
+ 'include_in_all' => false
+ ),
+ 'post_parent' => array(
+ 'type' => 'long',
+ 'index' => 'not_analyzed',
+ 'include_in_all' => false
+ ),
+ 'post_type' => array(
+ 'type' => 'multi_field',
+ 'fields' => array(
+ 'post_type' => array(
+ 'type' => 'string'
+ ),
+ 'raw' => array(
+ 'type' => 'string',
+ 'index' => 'not_analyzed',
+ 'include_in_all' => false
+ )
+ )
+ ),
+ 'post_mime_type' => array(
+ 'type' => 'string',
+ 'index' => 'not_analyzed',
+ 'include_in_all' => false
+ ),
+ 'permalink' => array(
+ 'type' => 'string'
+ ),
+ 'terms' => array(
+ "type" => "object"
+ ),
+ 'post_meta' => array(
+ 'type' => 'object'
+ ),
+ 'date_terms' => array(
+ "type" => "object",
+ "path" => "full",
+ 'fields' => array(
+ "year" => array( //4 digit year (e.g. 2011)
+ "type" => "integer",
+ ),
+ "month" => array( //Month number (from 1 to 12) alternate name "monthnum"
+ "type" => "integer",
+ ),
+ "m" => array( //YearMonth (For e.g.: 201307)
+ "type" => "integer",
+ ),
+ "week" => array( //Week of the year (from 0 to 53) alternate name "w"
+ "type" => "integer",
+ ),
+ "day" => array( //Day of the month (from 1 to 31)
+ "type" => "integer",
+ ),
+ "dayofweek" => array( //Accepts numbers 1-7 (1 is Sunday)
+ "type" => "integer",
+ ),
+ "dayofweek_iso" => array( //Accepts numbers 1-7 (1 is Monday)
+ "type" => "integer",
+ ),
+ "dayofyear" => array( //Accepts numbers 1-366
+ "type" => "integer",
+ ),
+ "hour" => array( //Hour (from 0 to 23)
+ "type" => "integer",
+ ),
+ "minute" => array( //Minute (from 0 to 59)
+ "type" => "integer",
+ ),
+ "second" => array( //Second (0 to 59)
+ "type" => "integer",
+ )
+ )
+ )
+ )
+ )
+ )
+);
diff --git a/readme.txt b/readme.txt
index 38d4d7cd90..b107876333 100644
--- a/readme.txt
+++ b/readme.txt
@@ -4,8 +4,8 @@ Author URI: http://10up.com
Plugin URI: https://github.com/10up/ElasticPress
Tags: search, elasticsearch, fuzzy, facet, searching, autosuggest, suggest, elastic, advanced search
Requires at least: 3.7.1
-Tested up to: 4.2
-Stable tag: 1.3.1
+Tested up to: 4.3
+Stable tag: 1.4
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
@@ -58,8 +58,27 @@ configuring single site and multi-site cross-site search are slightly different.
== Changelog ==
+= 1.4 =
+
+### Bug Fixes:
+
+* Duplicate sync post hooks separated. Props [superdummy](https://github.com/superdummy)
+* Don't send empty index error emails. Props [cmmarslender](https://github.com/cmmarslender)
+* Remove default shard and indices configuration numbers but maintain backwards compatibility. Props [zamoose](https://github.com/zamoose)
+* Fix wrong author ID in post data. Props [eduardmaghakyan](https://github.com/eduardmaghakyan)
+
+### Enhancements:
+
+* `date_query` and date parameters now supported in WP_Query. Props [joeyblake](https://github.com/joeyblake) and [eduardmaghakyan](https://github.com/eduardmaghakyan)
+* Make all request headers filterable
+* Add EP API key to all requests as a header if a constant is defined. Props [zamoose](https://github.com/zamoose)
+* Add index exists function; remove indexes on blog deletion/deactivation. Props [joeyblake](https://github.com/joeyblake)
+* Refactor wp-cli stats for multisite. Props [jaace](https://github.com/jaace)
+* Index mappings array moved to separate file. Props [mikaelmattsson](https://github.com/mikaelmattsson)
+* Support meta inequality comparisons. Props [psorensen](https://github.com/psorensen)
+
= 1.3.1 =
-* Support `date` in WP_Query `orderby`.
+* Support `date` in WP_Query `orderby`. Props [psorensen](https://github.com/psorensen)
= 1.3 =
* Support `meta_query` in WP_Query integration
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
index a0a2efabf3..1364a96914 100644
--- a/tests/bootstrap.php
+++ b/tests/bootstrap.php
@@ -9,8 +9,12 @@
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
-function ep_test_shard_number() {
- return 1;
+function ep_test_shard_number( $mapping ) {
+ $mapping['settings']['index'] = array(
+ 'number_of_shards' => 1,
+ );
+
+ return $mapping;
}
function _manually_load_plugin() {
@@ -23,7 +27,7 @@ function _manually_load_plugin() {
require( dirname( __FILE__ ) . '/../elasticpress.php' );
- add_filter( 'ep_default_index_number_of_shards', 'ep_test_shard_number' );
+ add_filter( 'ep_config_mapping', 'ep_test_shard_number' );
$tries = 5;
$sleep = 3;
diff --git a/tests/includes/functions.php b/tests/includes/functions.php
index 1227e21183..e74b0464f0 100644
--- a/tests/includes/functions.php
+++ b/tests/includes/functions.php
@@ -70,4 +70,56 @@ function ep_create_and_sync_post( $post_args = array(), $post_meta = array(), $s
}
return $post_id;
+}
+
+function ep_create_date_query_posts() {
+ $sites = ep_get_sites();
+ $beginning_tz = date_default_timezone_get();
+
+ date_default_timezone_set('America/Los_Angeles');
+
+ foreach ( $sites as $site ) {
+ switch_to_blog( $site['blog_id'] );
+
+ $post_date = strtotime( "January 6th, 2012 11:59PM" );
+
+ for( $i = 0; $i <= 10; ++$i ) {
+
+ ep_create_and_sync_post( array(
+ 'post_title' => 'post_title' . $site['blog_id'],
+ 'post_content' => 'findme',
+ 'post_date' => date( "Y-m-d H:i:s", strtotime( "-$i days", strtotime( "-$i hours", $post_date ) ) ),
+ 'post_date_gmt' => gmdate( "Y-m-d H:i:s", strtotime( "-$i days", strtotime( "-$i hours", $post_date ) ) ),
+ ) );
+
+ ep_refresh_index();
+ }
+
+ restore_current_blog();
+ }
+ date_default_timezone_set($beginning_tz);
+
+}
+
+/**
+ * Get all sites, count indexes
+ *
+ * @return array total index count with last blog id to manipulate blog with an index
+ */
+function ep_count_indexes() {
+ $sites = ep_get_sites();
+
+ $count_indexes = 0;
+ foreach ( $sites as $site ) {
+ if ( $index_name = ep_get_index_name( $site[ 'blog_id' ] ) ) {
+ if ( ep_index_exists( $index_name ) ) {
+ $count_indexes++;
+ $last_blog_id_with_index = $site[ 'blog_id' ];
+ }
+ }
+ }
+ return array(
+ 'total_indexes' => $count_indexes,
+ 'last_blog_id_with_index' => $last_blog_id_with_index,
+ );
}
\ No newline at end of file
diff --git a/tests/test-multisite.php b/tests/test-multisite.php
index b1e4d6958e..5f8962f93c 100644
--- a/tests/test-multisite.php
+++ b/tests/test-multisite.php
@@ -418,6 +418,453 @@ public function testWPQuerySearchExcerpt() {
$this->assertEquals( $query->found_posts, 2 );
}
+ /**
+ * Test a simple date param search by date and monthnum
+ *
+ */
+ public function testSimpleDateMonthNum() {
+ ep_create_date_query_posts();
+
+ $args = array(
+ 's' => 'findme',
+ 'sites' => 'all',
+ 'monthnum' => 12,
+ 'posts_per_page' => 100,
+ );
+
+ $query = new WP_Query( $args );
+ $this->assertEquals( $query->post_count, 15 );
+ $this->assertEquals( $query->found_posts, 15 );
+
+ $args = array(
+ 's' => 'findme',
+ 'sites' => 'all',
+ 'day' => 5,
+ 'posts_per_page' => 100,
+ );
+
+ $query = new WP_Query( $args );
+ $this->assertEquals( $query->post_count, 3 );
+ $this->assertEquals( $query->found_posts, 3 );
+ }
+
+ /**
+ * Test a simple date param search by day number of week
+ *
+ */
+ public function testSimpleDateDay() {
+ ep_create_date_query_posts();
+
+ $args = array(
+ 's' => 'findme',
+ 'sites' => 'all',
+ 'day' => 5,
+ 'posts_per_page' => 100,
+ );
+
+ $query = new WP_Query( $args );
+ $this->assertEquals( $query->post_count, 3 );
+ $this->assertEquals( $query->found_posts, 3 );
+ }
+
+ /**
+ * Test a date query with before and after range
+ *
+ */
+ public function testDateQueryBeforeAfter() {
+ ep_create_date_query_posts();
+
+ $args = array(
+ 's' => 'findme',
+ 'sites' => 'all',
+ 'posts_per_page' => 100,
+ 'date_query' => array(
+ array(
+ 'after' => 'January 1st, 2012',
+ 'before' => array(
+ 'year' => 2012,
+ 'day' => 2,
+ 'month' => 1,
+ 'hour' => 23,
+ 'minute' => 59,
+ 'second' => 59
+ ),
+ 'inclusive' => true,
+ ),
+ )
+ );
+
+ $query = new WP_Query( $args );
+ $this->assertEquals( $query->post_count, 6 );
+ $this->assertEquals( $query->found_posts, 6 );
+ }
+
+ /**
+ * Test a date query with multiple column range comparison
+ *
+ */
+ public function testDateQueryMultiColumn() {
+ ep_create_date_query_posts();
+
+ $args = array(
+ 's' => 'findme',
+ 'sites' => 'all',
+ 'posts_per_page' => 100,
+ 'date_query' => array(
+ array(
+ 'column' => 'post_date',
+ 'after' => 'January 1st 2012',
+ ),
+ array(
+ 'column' => 'post_date_gmt',
+ 'after' => 'January 3rd 2012 8AM',
+ ),
+ )
+ );
+
+ $query = new WP_Query( $args );
+
+ $this->assertEquals( $query->post_count, 12 );
+ $this->assertEquals( $query->found_posts, 12 );
+ }
+
+ /**
+ * Test a date query with multiple column range comparison inclusive
+ */
+ public function testDateQueryMultiColumnInclusive() {
+ ep_create_date_query_posts();
+
+ $args = array(
+ 's' => 'findme',
+ 'sites' => 'all',
+ 'posts_per_page' => 100,
+ 'date_query' => array(
+ array(
+ 'column' => 'post_date',
+ 'before' => 'January 5th 2012 11:00PM',
+ ),
+ array(
+ 'column' => 'post_date',
+ 'after' => 'January 5th 2012 10:00PM',
+ ),
+ 'inclusive' => true,
+ )
+ );
+
+ $query = new WP_Query( $args );
+ $this->assertEquals( $query->post_count, 3 );
+ $this->assertEquals( $query->found_posts, 3 );
+ }
+
+
+ /**
+ * Test a date query with multiple eltries
+ */
+ public function testDateQueryWorkingHours() {
+ ep_create_date_query_posts();
+
+ $args = array(
+ 's' => 'findme',
+ 'sites' => 'all',
+ 'posts_per_page' => 100,
+ 'date_query' => array(
+ array(
+ 'hour' => 9,
+ 'compare' => '>=',
+ ),
+ array(
+ 'hour' => 17,
+ 'compare' => '<=',
+ ),
+ array(
+ 'dayofweek' => array( 2, 6 ),
+ 'compare' => 'BETWEEN',
+ ),
+ ),
+ );
+
+ $query = new WP_Query( $args );
+
+ $this->assertEquals( $query->post_count, 15 );
+ $this->assertEquals( $query->found_posts, 15 );
+ }
+
+ /**
+ * Test a date query with multiple column range comparison not inclusive
+ */
+ public function testDateQueryMultiColumnNotInclusive() {
+ ep_create_date_query_posts();
+
+ $args = array(
+ 's' => 'findme',
+ 'sites' => 'all',
+ 'posts_per_page' => 100,
+ 'date_query' => array(
+ array(
+ 'column' => 'post_date',
+ 'before' => 'January 5th 2012',
+ ),
+ array(
+ 'column' => 'post_date',
+ 'after' => 'January 5th 2012',
+ ),
+ 'inclusive' => false,
+ )
+ );
+
+ $query = new WP_Query( $args );
+ $this->assertEquals( $query->post_count, 0 );
+ $this->assertEquals( $query->found_posts, 0 );
+ }
+
+ /**
+ * Test a simple date query search by year, monthnum and day of week
+ *
+ */
+ public function testDateQuerySimple() {
+ ep_create_date_query_posts();
+
+ $args = array(
+ 's' => 'findme',
+ 'sites' => 'all',
+ 'posts_per_page' => 100,
+ 'date_query' => array(
+ array(
+ 'year' => 2012,
+ 'monthnum' => 1,
+ 'day' => 1,
+ )
+ )
+ );
+
+ $query = new WP_Query( $args );
+ $this->assertEquals( $query->post_count, 3 );
+ $this->assertEquals( $query->found_posts, 3 );
+ }
+
+ /**
+ * Test a date query with BETWEEN comparison
+ *
+ */
+ public function testDateQueryBetween() {
+ ep_create_date_query_posts();
+
+ $args = array(
+ 's' => 'findme',
+ 'sites' => 'all',
+ 'posts_per_page' => 100,
+ 'date_query' => array(
+ array (
+ 'day' => array( 1, 5 ),
+ 'compare' => 'BETWEEN',
+ )
+ )
+ );
+
+ $query = new WP_Query( $args );
+ $this->assertEquals( $query->post_count, 15 );
+ $this->assertEquals( $query->found_posts, 15 );
+ }
+
+ /**
+ * Test a date query with NOT BETWEEN comparison
+ *
+ */
+ public function testDateQueryNotBetween() {
+ ep_create_date_query_posts();
+
+ $args = array(
+ 's' => 'findme',
+ 'sites' => 'all',
+ 'posts_per_page' => 100,
+ 'date_query' => array(
+ array (
+ 'day' => array( 1, 5 ),
+ 'compare' => 'NOT BETWEEN',
+ )
+ )
+ );
+
+ $query = new WP_Query( $args );
+ $this->assertEquals( $query->post_count, 24 );
+ $this->assertEquals( $query->found_posts, 24 );
+ }
+
+ /**
+ * Test a date query with BETWEEN comparison on 1 day range
+ *
+ */
+ public function testDateQueryShortBetween() {
+ ep_create_date_query_posts();
+
+ $args = array(
+ 's' => 'findme',
+ 'sites' => 'all',
+ 'posts_per_page' => 100,
+ 'date_query' => array(
+ array (
+ 'day' => array( 5, 5 ),
+ 'compare' => 'BETWEEN',
+ )
+ )
+ );
+
+ $query = new WP_Query( $args );
+
+ $this->assertEquals( 3, $query->post_count );
+ $this->assertEquals( 3, $query->found_posts );
+ }
+
+ /**
+ * Test a date query with multiple range comparisons
+ *
+ * Currently created posts don't have that many date based differences
+ * for this test
+ *
+ */
+ public function testDateQueryCompare() {
+ ep_create_date_query_posts();
+
+ $args = array(
+ 's' => 'findme',
+ 'sites' => 'all',
+ 'posts_per_page' => 100,
+ 'date_query' => array(
+ array(
+ 'monthnum' => 1,
+ 'compare' => '<=',
+ ),
+ array(
+ 'year' => 2012,
+ 'compare' => '>=',
+ ),
+ array(
+ 'day' => array( 2, 5 ),
+ 'compare' => 'BETWEEN',
+ ),
+ )
+ );
+
+ $query = new WP_Query( $args );
+ $this->assertEquals( $query->post_count, 12 );
+ $this->assertEquals( $query->found_posts, 12 );
+ }
+
+ /**
+ * Test a date query with multiple range comparisons where before and after are
+ * structured differently. Test inclusive range.
+ */
+ public function testDateQueryInclusiveTypeMix() {
+ ep_create_date_query_posts();
+
+ $args = array(
+ 's' => 'findme',
+ 'sites' => 'all',
+ 'posts_per_page' => 100,
+ 'date_query' => array(
+ array(
+ 'after' => 'January 4, 2012',
+ 'before' => array(
+ 'year' => 2012,
+ 'month' => 1,
+ 'day' => 5,
+ 'hour' => 23,
+ 'minute' => 0,
+ 'second' => 0
+ ),
+ 'inclusive' => true,
+ ),
+ )
+ );
+
+ $query = new WP_Query( $args );
+ $this->assertEquals( $query->post_count, 6 );
+ $this->assertEquals( $query->found_posts, 6 );
+ }
+
+ /**
+ * Test a date query with multiple range comparisons where before and after are
+ * structured differently. Test exclusive range.
+ */
+ public function testDateQueryExclusiveTypeMix() {
+ ep_create_date_query_posts();
+
+ $args = array(
+ 's' => 'findme',
+ 'sites' => 'all',
+ 'posts_per_page' => 100,
+ 'date_query' => array(
+ array(
+ 'after' => 'January 4, 2012 10:00PM',
+ 'before' => array(
+ 'year' => 2012,
+ 'month' => 1,
+ 'day' => 5,
+ ),
+ 'inclusive' => false,
+ ),
+ )
+ );
+
+ $query = new WP_Query( $args );
+ $this->assertEquals( $query->post_count, 0 );
+ $this->assertEquals( $query->found_posts, 0 );
+ }
+
+ /**
+ * Test another date query with multiple range comparisons
+ */
+ public function testDateQueryCompare2() {
+ ep_create_date_query_posts();
+
+ $args = array(
+ 's' => 'findme',
+ 'sites' => 'all',
+ 'posts_per_page' => 100,
+ 'date_query' => array(
+ array(
+ 'monthnum' => 1,
+ 'compare' => '<=',
+ ),
+ array(
+ 'year' => 2012,
+ 'compare' => '>=',
+ ),
+ array(
+ 'day' => array( 5, 6 ),
+ 'compare' => 'BETWEEN',
+ ),
+ )
+ );
+
+ $query = new WP_Query( $args );
+ $this->assertEquals( 6, $query->post_count );
+ $this->assertEquals( 6, $query->found_posts );
+ }
+
+ /**
+ * Test date query where posts are only pulled from weekdays
+ */
+ public function testDateQueryWeekdayRange() {
+ ep_create_date_query_posts();
+
+ $args = array(
+ 's' => 'findme',
+ 'sites' => 'all',
+ 'posts_per_page' => 100,
+ 'date_query' => array(
+ array(
+ 'dayofweek' => array( 2, 6 ),
+ 'compare' => 'BETWEEN',
+ ),
+ ),
+ );
+
+ $query = new WP_Query( $args );
+ $this->assertEquals( 27, $query->post_count );
+ $this->assertEquals( 27, $query->found_posts );
+ }
+
/**
* Test a tax query search
*
@@ -1227,4 +1674,75 @@ public function testQueryIntegrationSkip() {
$this->assertTrue( empty( $query->posts ) );
}
+
+ /**
+ * Test post object data
+ *
+ * @since 1.4
+ */
+ public function testPostObject() {
+ $sites = ep_get_sites();
+
+ foreach ( $sites as $site ) {
+ switch_to_blog( $site['blog_id'] );
+
+ ep_create_and_sync_post( array( 'post_title' => 'findme', 'post_author' => $site['blog_id'] ) );
+ ep_create_and_sync_post( array( 'post_title' => 'findme', 'post_author' => $site['blog_id'] ) );
+
+ ep_refresh_index();
+
+ restore_current_blog();
+ }
+
+ $args = array(
+ 's' => 'findme',
+ 'sites' => 'all',
+ 'posts_per_page' => 10,
+ );
+
+ $query = new WP_Query( $args );
+
+
+ $this->assertEquals( 6, $query->post_count );
+ $this->assertEquals( 6, $query->found_posts );
+
+ while ( $query->have_posts() ) {
+ $query->the_post();
+ global $post;
+
+ $this->assertEquals( $post->site_id, $post->post_author );
+ }
+ wp_reset_postdata();
+ }
+
+ /**
+ * Test index_exists helper function
+ */
+ public function testIndexExists() {
+ $sites = ep_get_sites();
+
+ $first_site_index = ep_get_index_name( $sites[0]['blog_id'] );
+ $index_should_exist = ep_index_exists( $first_site_index );
+ $index_should_not_exist = ep_index_exists( $first_site_index . 2 );
+
+ $this->assertTrue( $index_should_exist );
+ $this->assertFalse( $index_should_not_exist );
+ }
+
+ /**
+ * Tests Deletion of index when a blog is deleted
+ */
+ public function testDeleteIndex( ) {
+ $index_count = ep_count_indexes();
+
+ $count_indexes = $index_count['total_indexes'];
+ $last_blog_id = $index_count['last_blog_id_with_index'];
+
+ wpmu_delete_blog( $last_blog_id );
+
+ $post_delete_count = ep_count_indexes();
+ $post_count_indexes = $post_delete_count['total_indexes'];
+
+ $this->assertNotEquals( $count_indexes, $post_count_indexes );
+ }
}
\ No newline at end of file
diff --git a/tests/test-single-site.php b/tests/test-single-site.php
index 28203d5c75..56e9cdf028 100644
--- a/tests/test-single-site.php
+++ b/tests/test-single-site.php
@@ -1103,6 +1103,122 @@ public function testMetaQueryNotExists() {
$this->assertEquals( 2, $query->found_posts );
}
+ /**
+ * Test a query that searches and filters by a meta greater than to query
+ *
+ * @since 1.4
+ */
+ public function testMetaQueryGreaterThan() {
+ ep_create_and_sync_post( array( 'post_content' => 'the post content findme' ) );
+ ep_create_and_sync_post( array( 'post_content' => 'the post content findme' ) );
+ ep_create_and_sync_post( array( 'post_content' => 'post content findme' ), array( 'test_key' => '100' ) );
+ ep_create_and_sync_post( array( 'post_content' => 'post content findme' ), array( 'test_key' => '101' ) );
+
+ ep_refresh_index();
+ $args = array(
+ 's' => 'findme',
+ 'meta_query' => array(
+ array(
+ 'key' => 'test_key',
+ 'value' => '100',
+ 'compare' => '>',
+ )
+ ),
+ );
+
+ $query = new WP_Query( $args );
+
+ $this->assertEquals( 1, $query->post_count );
+ $this->assertEquals( 1, $query->found_posts );
+ }
+
+ /**
+ * Test a query that searches and filters by a meta greater than or equal to query
+ *
+ * @since 1.4
+ */
+ public function testMetaQueryGreaterThanEqual() {
+ ep_create_and_sync_post( array( 'post_content' => 'the post content findme' ) );
+ ep_create_and_sync_post( array( 'post_content' => 'the post content findme' ) );
+ ep_create_and_sync_post( array( 'post_content' => 'post content findme' ), array( 'test_key' => '100' ) );
+ ep_create_and_sync_post( array( 'post_content' => 'post content findme' ), array( 'test_key' => '101' ) );
+
+ ep_refresh_index();
+ $args = array(
+ 's' => 'findme',
+ 'meta_query' => array(
+ array(
+ 'key' => 'test_key',
+ 'value' => '100',
+ 'compare' => '>=',
+ )
+ ),
+ );
+
+ $query = new WP_Query( $args );
+
+ $this->assertEquals( 2, $query->post_count );
+ $this->assertEquals( 2, $query->found_posts );
+ }
+
+ /**
+ * Test a query that searches and filters by a meta less than to query
+ *
+ * @since 1.4
+ */
+ public function testMetaQueryLessThan() {
+ ep_create_and_sync_post( array( 'post_content' => 'the post content findme' ) );
+ ep_create_and_sync_post( array( 'post_content' => 'the post content findme' ) );
+ ep_create_and_sync_post( array( 'post_content' => 'post content findme' ), array( 'test_key' => '100' ) );
+ ep_create_and_sync_post( array( 'post_content' => 'post content findme' ), array( 'test_key' => '101' ) );
+
+ ep_refresh_index();
+ $args = array(
+ 's' => 'findme',
+ 'meta_query' => array(
+ array(
+ 'key' => 'test_key',
+ 'value' => '101',
+ 'compare' => '<',
+ )
+ ),
+ );
+
+ $query = new WP_Query( $args );
+
+ $this->assertEquals( 1, $query->post_count );
+ $this->assertEquals( 1, $query->found_posts );
+ }
+
+ /**
+ * Test a query that searches and filters by a meta less than or equal to query
+ *
+ * @since 1.4
+ */
+ public function testMetaQueryLessThanEqual() {
+ ep_create_and_sync_post( array( 'post_content' => 'the post content findme' ) );
+ ep_create_and_sync_post( array( 'post_content' => 'the post content findme' ) );
+ ep_create_and_sync_post( array( 'post_content' => 'post content findme' ), array( 'test_key' => '100' ) );
+ ep_create_and_sync_post( array( 'post_content' => 'post content findme' ), array( 'test_key' => '101' ) );
+
+ ep_refresh_index();
+ $args = array(
+ 's' => 'findme',
+ 'meta_query' => array(
+ array(
+ 'key' => 'test_key',
+ 'value' => '101',
+ 'compare' => '<=',
+ )
+ ),
+ );
+
+ $query = new WP_Query( $args );
+
+ $this->assertEquals( 2, $query->post_count );
+ $this->assertEquals( 2, $query->found_posts );
+ }
+
/**
* Test an advanced meta filter query
*