forked from algolia/algoliasearch-wordpress
-
Notifications
You must be signed in to change notification settings - Fork 0
/
class-algolia-search.php
186 lines (156 loc) · 4.71 KB
/
class-algolia-search.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
<?php
class Algolia_Search
{
/**
* @var int
*/
private $nb_hits;
/**
* @var array
*/
private $ranked_post_ids;
/**
* @var Algolia_Index
*/
private $index;
/**
* @param Algolia_Index $index
*/
public function __construct( Algolia_Index $index ) {
$this->index = $index;
add_action( 'pre_get_posts', array( $this, 'pre_get_posts' ) );
}
/**
* Determines if we should filter the query passed as argument.
*
* @param WP_Query $query
*
* @return bool
*/
private function should_filter_query( WP_Query $query ) {
return ! $query->is_admin && $query->is_search() && $query->is_main_query();
}
/**
* We force the WP_Query to only return records according to Algolia's ranking.
*
* @param WP_Query $query
*/
public function pre_get_posts( WP_Query $query ) {
if ( ! $this->should_filter_query( $query ) ) {
return;
}
$current_page = 1;
if ( get_query_var( 'paged' ) ) {
$current_page = get_query_var( 'paged' );
} elseif ( get_query_var( 'page' ) ) {
$current_page = get_query_var( 'page' );
}
$posts_per_page = (int) get_option( 'posts_per_page' );
$params = apply_filters( 'algolia_search_params', array(
'attributesToRetrieve' => 'post_id',
'hitsPerPage' => $posts_per_page,
'page' => $current_page - 1, // Algolia pages are zero indexed.
) );
$order_by = apply_filters( 'algolia_search_order_by', null );
$order = apply_filters( 'algolia_search_order', 'desc' );
try {
$results = $this->index->search( $query->query['s'], $params, $order_by, $order );
} catch ( \AlgoliaSearch\AlgoliaException $exception ) {
error_log( $exception->getMessage() );
return;
}
add_filter( 'the_posts', array( $this, 'the_posts' ), 10, 2 );
add_filter( 'found_posts', array( $this, 'found_posts' ), 10, 2 );
add_filter( 'posts_search', array( $this, 'posts_search' ), 10, 2 );
add_filter( 'posts_clauses', array( $this, 'posts_clauses' ), 10, 2 );
// Store the total number of its, so that we can hook into the `found_posts`.
// This is useful for pagination.
$this->nb_hits = $results['nbHits'];
$post_ids = array();
foreach ( $results['hits'] as $result ) {
$post_ids[] = $result['post_id'];
}
$this->ranked_post_ids = $post_ids;
$query->set( 'posts_per_page', $posts_per_page );
$query->set( 'offset', 0 );
$post_types = 'any';
if ( isset( $_GET['post_type'] ) ) {
$post_type = get_post_type_object( $_GET['post_type'] );
if (null !== $post_type) {
$post_types = $post_type->name;
}
}
$query->set( 'post_type', $post_types );
$query->set( 'post__in', $post_ids );
// Todo: this actually still excludes trash and auto-drafts.
$query->set( 'post_status', 'any' );
}
/**
* This hook orders the posts according to Algolia's ranking instead
* of the MySQL default ranking.
*
* @param array $posts
* @param WP_Query $query
*
* @return array
*/
public function the_posts( array $posts, WP_Query $query ) {
if ( ! $this->should_filter_query( $query ) ) {
return $posts;
}
$ranked_posts = array();
$ranked_post_ids = array_reverse( $this->ranked_post_ids );
do {
$ranked_post_id = array_pop( $ranked_post_ids );
foreach ( $posts as $post ) {
// Todo: This could be optimized.
if ( $ranked_post_id === $post->ID ) {
$ranked_posts[] = $post;
break;
}
}
} while ( ! empty( $ranked_post_ids ) );
return $ranked_posts;
}
/**
* This hook returns the actual real number of results available in Algolia.
*
* @param int $found_posts
* @param WP_Query $query
*
* @return int
*/
public function found_posts( $found_posts, WP_Query $query ) {
return $this->should_filter_query( $query ) ? $this->nb_hits : $found_posts;
}
/**
* Filter the search SQL that is used in the WHERE clause of WP_Query.
* Removes the where Like part of the queries as we consider Algolia as being the source of truth.
* We don't want to filter by anything but the actual list of post_ids resulting
* from the Algolia search.
*
* @param string $search
* @param WP_Query $query
*
* @return string
*/
public function posts_search( $search, WP_Query $query ) {
return $this->should_filter_query( $query ) ? '' : $search;
}
/**
* Removes remaining unused SQL pieces.
*
* @param array $pieces
* @param WP_Query $query
*
* @return mixed
*/
public function posts_clauses( $pieces, WP_Query $query ) {
if ( ! $this->should_filter_query( $query ) ) {
return $pieces;
}
// We don't care about MySQL ordering. We will need to re-order with Algolia's ranking anyway.
$pieces['orderby'] = '';
return $pieces;
}
}