Version Description
- New feature: New filter hook
relevanssi_phrase
filters each phrase before it's used in the MySQL query. - New feature: Relevanssi can now add Google-style missing term lists to the search results. You can either use the
%missing%
tag in the search results breakdown settings, or you can create your own code: the missing terms are also in$post->missing_terms
. Relevanssi Premium will also add "Must have" links when there's just one missing term. - New feature: New filter hook
relevanssi_missing_terms_tag
controls which tag is used to wrap the missing terms. - New feature: New filter hook
relevanssi_missing_terms_template
can be used to filter the template used to display the missing terms. - New feature: New function
relevanssi_get_post_meta_for_all_posts()
can be used to fetch particular meta field for a number of posts with just one query. - New feature: New filter hook
relevanssi_post_author
lets you filter the post author display_name before it is indexed. - Changed behaviour:
relevanssi_strip_tags()
used to add spaces between HTML tags before stripping them. It no longer does that, but instead adds a space after specific list of tags (p, br, h1-h6, div, blockquote, hr, li, img) to avoid words being stuck to each other in excerpts. - Changed behaviour: Relevanssi now indexes the contents of Oxygen Builder PHP & HTML code blocks.
- Changed behaviour: Relevanssi now handles synonyms inside phrases differently. If the new filter hook
relevanssi_phrase_synonyms
returnstrue
(default value), synonyms create a new phrase (with synonym 'dog=hound', phrase"dog biscuits"
becomes"dog biscuits" "hound biscuits"
). If the value isfalse
, synonyms inside phrases are ignored. - Minor fix: Warnings when creating excerpts with search terms that contain a slash were removed.
- Minor fix: Better Ninja Tables compatibility to avoid problems with lightbox images.
- Minor fix: Relevanssi did not work well in the Media Library grid view. Relevanssi is now blocked there. If you need Relevanssi in Media Library searches, use the list view.
- Minor fix: Relevanssi excerpt creation didn't work correctly when numerical search terms were used.
Download this release
Release Info
Developer | msaari |
Plugin | Relevanssi – A Better Search |
Version | 4.13.0 |
Comparing to | |
See all releases |
Code changes from version 4.12.5 to 4.13.0
- changelog.txt +24 -0
- lib/common.php +155 -56
- lib/compatibility/ninjatables.php +11 -1
- lib/compatibility/oxygen.php +23 -0
- lib/excerpts-highlights.php +69 -3
- lib/indexing.php +12 -2
- lib/init.php +1 -0
- lib/phrases.php +11 -1
- lib/search.php +842 -611
- lib/tabs/searching-tab.php +6 -2
- lib/utils.php +102 -4
- readme.txt +21 -33
- relevanssi.php +2 -2
changelog.txt
CHANGED
@@ -1,3 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
= 4.8.3 =
|
2 |
* New feature: Both `relevanssi_fuzzy_query` and `relevanssi_term_where` now get the current search term as a parameter.
|
3 |
* Minor fix: Relevanssi database tables don't have PRIMARY keys, only UNIQUE keys. In case this is a problem (for example on Digital Ocean servers), deactivate and activate Relevanssi to fix the problem.
|
1 |
+
= 4.9.1 =
|
2 |
+
* Changed behaviour: The `relevanssi_excerpt_part` filter hook now gets the post ID as a second parameter. The documentation for the filter has been fixed to match actual use: this filter is applied to the excerpt part after the highlighting and the ellipsis have been added.
|
3 |
+
* Changed behaviour: The `relevanssi_index_custom_fields` filter hook is no longer used when determining which custom fields are used for phrase searching. If you have a use case where this change matters, please contact us.
|
4 |
+
* Minor fix: The `relevanssi_excerpt` filter hook was removed in 4.9.0. It is now restored and behaves the way it did before.
|
5 |
+
* Minor fix: Avoids undefined variable warnings from the Pretty Links compatibility code.
|
6 |
+
* Minor fix: The Oxygen Builder compatibility has been improved. Now shortcodes in Oxygen Builder content are expanded, if that setting is enabled in Relevanssi settings.
|
7 |
+
|
8 |
+
= 4.9.0 =
|
9 |
+
* New feature: There's now a "Debugging" tab in the Relevanssi settings, letting you see how the Relevanssi index sees posts. This is familiar to Premium users, but is now available in the free version as well.
|
10 |
+
* New feature: The SEO Framework plugin is now supported and posts set excluded from the search in SEO Framework settings will be excluded from the index.
|
11 |
+
* New feature: There's a new option, "Expand highlights". Enabling it makes Relevanssi expand partial-word highlights to cover the full word. This is useful when doing partial matching and when using a stemmer.
|
12 |
+
* New feature: New filter hook `relevanssi_excerpt_part` allows you to modify the excerpt parts before they are combined together. This doesn't do much in the free version.
|
13 |
+
* New feature: Improved compatibility with Oxygen Builder. Relevanssi automatically indexes the Oxygen Builder content and cleans it up. New filter hooks `relevanssi_oxygen_section_filters` and `relevanssi_oxygen_section_content` allow easier filtering of Oxygen content to eg. remove unwanted sections.
|
14 |
+
* Changed behaviour: The "Uncheck this for non-ASCII highlights" option has been removed. Highlights are now done in a slightly different way that should work in all cases, including for example Cyrillic text, thus this option is no longer necessary.
|
15 |
+
* Minor fix: Fixes phrase searching using non-US alphabet.
|
16 |
+
* Minor fix: Relevanssi would break admin searching for hierarchical post types. This is now fixed, Relevanssi won't do that anymore.
|
17 |
+
* Minor fix: Relevanssi indexing now survives better shortcodes that change the global `$post`.
|
18 |
+
* Minor fix: Warnings about missing `relevanssi_update_counts` function are now removed.
|
19 |
+
* Minor fix: Paid Membership Pro support now takes notice of the "filter queries" setting.
|
20 |
+
* Minor fix: OR logic didn't work correctly when two phrases both had the same word (for example "freedom of speech" and "free speech"). The search would always be an AND search in those cases. That has been fixed.
|
21 |
+
* Minor fix: Relevanssi no longer blocks the Pretty Links admin page search.
|
22 |
+
* Minor fix: The "Respect 'exclude_from_search'" setting did not work if no post type parameter was included in the search parameters.
|
23 |
+
* Minor fix: The category inclusion and exclusion setting checkboxes on the Searching tab didn't work. The setting was saved, but the checkboxes wouldn't appear.
|
24 |
+
|
25 |
= 4.8.3 =
|
26 |
* New feature: Both `relevanssi_fuzzy_query` and `relevanssi_term_where` now get the current search term as a parameter.
|
27 |
* Minor fix: Relevanssi database tables don't have PRIMARY keys, only UNIQUE keys. In case this is a problem (for example on Digital Ocean servers), deactivate and activate Relevanssi to fix the problem.
|
lib/common.php
CHANGED
@@ -19,59 +19,22 @@
|
|
19 |
* @param array $data The source data.
|
20 |
*/
|
21 |
function relevanssi_add_matches( &$post, $data ) {
|
22 |
-
$hits
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
if ( isset( $data['body_matches'][ $post->ID ] ) ) {
|
39 |
-
$hits['body'] = $data['body_matches'][ $post->ID ];
|
40 |
-
}
|
41 |
-
if ( isset( $data['title_matches'][ $post->ID ] ) ) {
|
42 |
-
$hits['title'] = $data['title_matches'][ $post->ID ];
|
43 |
-
}
|
44 |
-
if ( isset( $data['tag_matches'][ $post->ID ] ) ) {
|
45 |
-
$hits['taxonomy']['tag'] = $data['tag_matches'][ $post->ID ];
|
46 |
-
}
|
47 |
-
if ( isset( $data['category_matches'][ $post->ID ] ) ) {
|
48 |
-
$hits['taxonomy']['category'] = $data['category_matches'][ $post->ID ];
|
49 |
-
}
|
50 |
-
if ( isset( $data['taxonomy_matches'][ $post->ID ] ) ) {
|
51 |
-
$hits['taxonomy']['taxonomy'] = $data['taxonomy_matches'][ $post->ID ];
|
52 |
-
}
|
53 |
-
if ( isset( $data['comment_matches'][ $post->ID ] ) ) {
|
54 |
-
$hits['comment'] = $data['comment_matches'][ $post->ID ];
|
55 |
-
}
|
56 |
-
if ( isset( $data['author_matches'][ $post->ID ] ) ) {
|
57 |
-
$hits['author'] = $data['author_matches'][ $post->ID ];
|
58 |
-
}
|
59 |
-
if ( isset( $data['excerpt_matches'][ $post->ID ] ) ) {
|
60 |
-
$hits['excerpt'] = $data['excerpt_matches'][ $post->ID ];
|
61 |
-
}
|
62 |
-
if ( isset( $data['customfield_matches'][ $post->ID ] ) ) {
|
63 |
-
$hits['customfield'] = $data['customfield_matches'][ $post->ID ];
|
64 |
-
}
|
65 |
-
if ( isset( $data['mysqlcolumn_matches'][ $post->ID ] ) ) {
|
66 |
-
$hits['mysqlcolumn'] = $data['mysqlcolumn_matches'][ $post->ID ];
|
67 |
-
}
|
68 |
-
if ( isset( $data['doc_weights'][ $post->ID ] ) ) {
|
69 |
-
$hits['score'] = round( $data['doc_weights'][ $post->ID ], 2 );
|
70 |
-
}
|
71 |
-
if ( isset( $data['term_hits'][ $post->ID ] ) ) {
|
72 |
-
$hits['terms'] = $data['term_hits'][ $post->ID ];
|
73 |
-
arsort( $hits['terms'] );
|
74 |
-
}
|
75 |
$post->relevanssi_hits = $hits;
|
76 |
}
|
77 |
|
@@ -94,6 +57,7 @@ function relevanssi_show_matches( $post ) {
|
|
94 |
}
|
95 |
|
96 |
$text = stripslashes( get_option( 'relevanssi_show_matches_text' ) );
|
|
|
97 |
$replace_these = array(
|
98 |
'%body%',
|
99 |
'%title%',
|
@@ -108,6 +72,7 @@ function relevanssi_show_matches( $post ) {
|
|
108 |
'%score%',
|
109 |
'%terms%',
|
110 |
'%total%',
|
|
|
111 |
);
|
112 |
$replacements = array(
|
113 |
$post->relevanssi_hits['body'],
|
@@ -123,6 +88,7 @@ function relevanssi_show_matches( $post ) {
|
|
123 |
$post->relevanssi_hits['score'],
|
124 |
$term_hits,
|
125 |
$total_hits,
|
|
|
126 |
);
|
127 |
$result = ' ' . str_replace( $replace_these, $replacements, $text );
|
128 |
|
@@ -137,6 +103,56 @@ function relevanssi_show_matches( $post ) {
|
|
137 |
return apply_filters( 'relevanssi_show_matches', $result );
|
138 |
}
|
139 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
140 |
/**
|
141 |
* Checks whether the user is allowed to see the post.
|
142 |
*
|
@@ -238,7 +254,7 @@ function relevanssi_populate_array( $matches, $blog_id = -1 ) {
|
|
238 |
foreach ( $matches as $match ) {
|
239 |
$cache_id = $blog_id . '|' . $match->doc;
|
240 |
if ( $match->doc > 0 && ! isset( $relevanssi_post_array[ $cache_id ] ) ) {
|
241 |
-
|
242 |
}
|
243 |
}
|
244 |
|
@@ -805,6 +821,40 @@ function relevanssi_add_synonyms( $query ) {
|
|
805 |
}
|
806 |
|
807 |
if ( count( $synonyms ) > 0 ) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
808 |
$new_terms = array();
|
809 |
$terms = array_keys( relevanssi_tokenize( $query, false ) ); // Remove stopwords is false here.
|
810 |
if ( ! in_array( $query, $terms, true ) ) {
|
@@ -823,6 +873,12 @@ function relevanssi_add_synonyms( $query ) {
|
|
823 |
}
|
824 |
}
|
825 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
826 |
if ( count( $new_terms ) > 0 ) {
|
827 |
$new_terms = array_unique( $new_terms );
|
828 |
foreach ( $new_terms as $new_term ) {
|
@@ -832,7 +888,7 @@ function relevanssi_add_synonyms( $query ) {
|
|
832 |
}
|
833 |
}
|
834 |
|
835 |
-
return $query;
|
836 |
}
|
837 |
|
838 |
/**
|
@@ -960,7 +1016,7 @@ function relevanssi_permalink( $link, $link_post = null ) {
|
|
960 |
$link = $link_post->relevanssi_link;
|
961 |
}
|
962 |
|
963 |
-
if ( is_search() &&
|
964 |
$link = relevanssi_add_highlight( $link, $link_post );
|
965 |
}
|
966 |
return $link;
|
@@ -1065,11 +1121,13 @@ function relevanssi_get_forbidden_post_types() {
|
|
1065 |
'jp_mem_plan', // Jetpack.
|
1066 |
'tablepress_table', // TablePress.
|
1067 |
'ninja-table', // Ninja Tables.
|
|
|
1068 |
'shop_order', // WooCommerce.
|
1069 |
'shop_order_refund', // WooCommerce.
|
1070 |
'wc_order_status', // WooCommerce.
|
1071 |
'wc_order_email', // WooCommerce.
|
1072 |
'shop_webhook', // WooCommerce.
|
|
|
1073 |
'et_theme_builder', // Divi.
|
1074 |
'et_template', // Divi.
|
1075 |
'et_header_layout', // Divi.
|
@@ -1110,6 +1168,11 @@ function relevanssi_get_forbidden_post_types() {
|
|
1110 |
'um_form', // Ultimate Member.
|
1111 |
'um_directory', // Ultimate Member.
|
1112 |
'mailpoet_page', // Mailpoet Page.
|
|
|
|
|
|
|
|
|
|
|
1113 |
);
|
1114 |
}
|
1115 |
|
@@ -1128,6 +1191,9 @@ function relevanssi_get_forbidden_taxonomies() {
|
|
1128 |
'amp_template', // AMP.
|
1129 |
'edd_commission_status', // Easy Digital Downloads.
|
1130 |
'edd_log_type', // Easy Digital Downloads.
|
|
|
|
|
|
|
1131 |
);
|
1132 |
}
|
1133 |
|
@@ -1568,3 +1634,36 @@ function relevanssi_update_synonyms_setting() {
|
|
1568 |
$array_synonyms[ $current_language ] = $synonyms;
|
1569 |
update_option( 'relevanssi_synonyms', $array_synonyms );
|
1570 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
* @param array $data The source data.
|
20 |
*/
|
21 |
function relevanssi_add_matches( &$post, $data ) {
|
22 |
+
$hits['body'] = $data['body_matches'][ $post->ID ] ?? 0;
|
23 |
+
$hits['title'] = $data['title_matches'][ $post->ID ] ?? 0;
|
24 |
+
$hits['taxonomy']['tag'] = $data['tag_matches'][ $post->ID ] ?? 0;
|
25 |
+
$hits['taxonomy']['category'] = $data['category_matches'][ $post->ID ] ?? 0;
|
26 |
+
$hits['taxonomy']['taxonomy'] = $data['taxonomy_matches'][ $post->ID ] ?? 0;
|
27 |
+
$hits['comment'] = $data['comment_matches'][ $post->ID ] ?? 0;
|
28 |
+
$hits['author'] = $data['author_matches'][ $post->ID ] ?? 0;
|
29 |
+
$hits['excerpt'] = $data['excerpt_matches'][ $post->ID ] ?? 0;
|
30 |
+
$hits['customfield'] = $data['customfield_matches'][ $post->ID ] ?? 0;
|
31 |
+
$hits['mysqlcolumn'] = $data['mysqlcolumn_matches'][ $post->ID ] ?? 0;
|
32 |
+
$hits['score'] = isset( $data['doc_weights'][ $post->ID ] ) ? round( $data['doc_weights'][ $post->ID ], 2 ) : 0;
|
33 |
+
$hits['terms'] = $data['term_hits'][ $post->ID ] ?? array();
|
34 |
+
$hits['missing_terms'] = $data['missing_terms'][ $post->ID ] ?? array();
|
35 |
+
|
36 |
+
arsort( $hits['terms'] );
|
37 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
$post->relevanssi_hits = $hits;
|
39 |
}
|
40 |
|
57 |
}
|
58 |
|
59 |
$text = stripslashes( get_option( 'relevanssi_show_matches_text' ) );
|
60 |
+
$missing_terms = strstr( $text, '%missing%' ) !== false ? relevanssi_generate_missing_terms_list( $post ) : '';
|
61 |
$replace_these = array(
|
62 |
'%body%',
|
63 |
'%title%',
|
72 |
'%score%',
|
73 |
'%terms%',
|
74 |
'%total%',
|
75 |
+
'%missing%',
|
76 |
);
|
77 |
$replacements = array(
|
78 |
$post->relevanssi_hits['body'],
|
88 |
$post->relevanssi_hits['score'],
|
89 |
$term_hits,
|
90 |
$total_hits,
|
91 |
+
$missing_terms,
|
92 |
);
|
93 |
$result = ' ' . str_replace( $replace_these, $replacements, $text );
|
94 |
|
103 |
return apply_filters( 'relevanssi_show_matches', $result );
|
104 |
}
|
105 |
|
106 |
+
/**
|
107 |
+
* Generates the "Missing:" element for the search results breakdown.
|
108 |
+
*
|
109 |
+
* @param WP_Post $post The post object, which should have the missing terms in
|
110 |
+
* $post->relevanssi_hits['missing_terms'].
|
111 |
+
*
|
112 |
+
* @return string The missing terms.
|
113 |
+
*/
|
114 |
+
function relevanssi_generate_missing_terms_list( $post ) {
|
115 |
+
$missing_terms = '';
|
116 |
+
if ( ! empty( $post->relevanssi_hits['missing_terms'] ) ) {
|
117 |
+
$missing_terms_list = implode(
|
118 |
+
' ',
|
119 |
+
array_map(
|
120 |
+
function ( $term ) {
|
121 |
+
/**
|
122 |
+
* Determines the tag used for missing terms, default <s>.
|
123 |
+
*
|
124 |
+
* @param string The tag, without angle brackets. Default 's'.
|
125 |
+
*/
|
126 |
+
$tag = apply_filters( 'relevanssi_missing_terms_tag', 's' );
|
127 |
+
return $tag ? "<$tag>$term</$tag>" : $term;
|
128 |
+
},
|
129 |
+
$post->relevanssi_hits['missing_terms']
|
130 |
+
)
|
131 |
+
);
|
132 |
+
$missing_terms = sprintf(
|
133 |
+
/**
|
134 |
+
* Filters the template for showing missing terms. Make sure you
|
135 |
+
* include the '%s', as that is where the missing terms will be
|
136 |
+
* inserted.
|
137 |
+
*
|
138 |
+
* @param string The template.
|
139 |
+
*/
|
140 |
+
apply_filters(
|
141 |
+
'relevanssi_missing_terms_template',
|
142 |
+
'<span class="missing_terms">' . __( 'Missing', 'relevanssi' ) . ': %s</span>'
|
143 |
+
),
|
144 |
+
$missing_terms_list
|
145 |
+
);
|
146 |
+
}
|
147 |
+
if (
|
148 |
+
1 === count( $post->relevanssi_hits['missing_terms'] )
|
149 |
+
&& function_exists( 'relevanssi_add_must_have' )
|
150 |
+
) {
|
151 |
+
$missing_terms .= relevanssi_add_must_have( $post );
|
152 |
+
}
|
153 |
+
return $missing_terms;
|
154 |
+
}
|
155 |
+
|
156 |
/**
|
157 |
* Checks whether the user is allowed to see the post.
|
158 |
*
|
254 |
foreach ( $matches as $match ) {
|
255 |
$cache_id = $blog_id . '|' . $match->doc;
|
256 |
if ( $match->doc > 0 && ! isset( $relevanssi_post_array[ $cache_id ] ) ) {
|
257 |
+
$ids[] = $match->doc;
|
258 |
}
|
259 |
}
|
260 |
|
821 |
}
|
822 |
|
823 |
if ( count( $synonyms ) > 0 ) {
|
824 |
+
$query = str_replace( array( '”', '“' ), '"', $query );
|
825 |
+
$phrases = relevanssi_extract_phrases( $query );
|
826 |
+
$new_phrases = array();
|
827 |
+
/**
|
828 |
+
* Controls how synonyms are handled when they appear inside
|
829 |
+
* phrases.
|
830 |
+
*
|
831 |
+
* @param bool If true, synonyms inside phrases create new phrases.
|
832 |
+
* If false, synonyms inside phrases are ignored.
|
833 |
+
*/
|
834 |
+
if ( apply_filters( 'relevanssi_phrase_synonyms', true ) ) {
|
835 |
+
foreach ( $phrases as $phrase ) {
|
836 |
+
$new_phrases[] = $phrase;
|
837 |
+
$words = explode( ' ', $phrase );
|
838 |
+
foreach ( array_keys( $synonyms ) as $synonym_source ) {
|
839 |
+
if ( in_array( $synonym_source, $words, true ) ) {
|
840 |
+
foreach ( array_keys( $synonyms[ $synonym_source ] ) as $synonym_replacement ) {
|
841 |
+
$new_phrases[] = str_replace( $synonym_source, $synonym_replacement, $phrase );
|
842 |
+
}
|
843 |
+
}
|
844 |
+
}
|
845 |
+
}
|
846 |
+
} else {
|
847 |
+
$new_phrases = $phrases;
|
848 |
+
}
|
849 |
+
|
850 |
+
$query = trim(
|
851 |
+
str_replace(
|
852 |
+
array_map( 'relevanssi_add_quotes', $phrases ),
|
853 |
+
'',
|
854 |
+
$query
|
855 |
+
)
|
856 |
+
);
|
857 |
+
|
858 |
$new_terms = array();
|
859 |
$terms = array_keys( relevanssi_tokenize( $query, false ) ); // Remove stopwords is false here.
|
860 |
if ( ! in_array( $query, $terms, true ) ) {
|
873 |
}
|
874 |
}
|
875 |
}
|
876 |
+
if ( count( $new_phrases ) > 0 ) {
|
877 |
+
$new_terms = array_merge(
|
878 |
+
$new_terms,
|
879 |
+
array_map( 'relevanssi_add_quotes', $new_phrases )
|
880 |
+
);
|
881 |
+
}
|
882 |
if ( count( $new_terms ) > 0 ) {
|
883 |
$new_terms = array_unique( $new_terms );
|
884 |
foreach ( $new_terms as $new_term ) {
|
888 |
}
|
889 |
}
|
890 |
|
891 |
+
return trim( $query );
|
892 |
}
|
893 |
|
894 |
/**
|
1016 |
$link = $link_post->relevanssi_link;
|
1017 |
}
|
1018 |
|
1019 |
+
if ( is_search() && property_exists( $link_post, 'relevance_score' ) ) {
|
1020 |
$link = relevanssi_add_highlight( $link, $link_post );
|
1021 |
}
|
1022 |
return $link;
|
1121 |
'jp_mem_plan', // Jetpack.
|
1122 |
'tablepress_table', // TablePress.
|
1123 |
'ninja-table', // Ninja Tables.
|
1124 |
+
'shop_coupon', // WooCommerce.
|
1125 |
'shop_order', // WooCommerce.
|
1126 |
'shop_order_refund', // WooCommerce.
|
1127 |
'wc_order_status', // WooCommerce.
|
1128 |
'wc_order_email', // WooCommerce.
|
1129 |
'shop_webhook', // WooCommerce.
|
1130 |
+
'woo_product_tab', // Woo Product Tab.
|
1131 |
'et_theme_builder', // Divi.
|
1132 |
'et_template', // Divi.
|
1133 |
'et_header_layout', // Divi.
|
1168 |
'um_form', // Ultimate Member.
|
1169 |
'um_directory', // Ultimate Member.
|
1170 |
'mailpoet_page', // Mailpoet Page.
|
1171 |
+
'mc4wp_form', // MailChimp.
|
1172 |
+
'elementor_font', // Elementor.
|
1173 |
+
'elementor_icons', // Elementor.
|
1174 |
+
'elementor_library', // Elementor.
|
1175 |
+
'elementor_snippet', // Elementor.
|
1176 |
);
|
1177 |
}
|
1178 |
|
1191 |
'amp_template', // AMP.
|
1192 |
'edd_commission_status', // Easy Digital Downloads.
|
1193 |
'edd_log_type', // Easy Digital Downloads.
|
1194 |
+
'elementor_library_type', // Elementor.
|
1195 |
+
'elementor_library_category', // Elementor.
|
1196 |
+
'elementor_font_type', // Elementor.
|
1197 |
);
|
1198 |
}
|
1199 |
|
1634 |
$array_synonyms[ $current_language ] = $synonyms;
|
1635 |
update_option( 'relevanssi_synonyms', $array_synonyms );
|
1636 |
}
|
1637 |
+
|
1638 |
+
/**
|
1639 |
+
* Replaces synonyms in an array with their original counterparts.
|
1640 |
+
*
|
1641 |
+
* If there's a synonym "dog=hound", and the array of terms contains "hound",
|
1642 |
+
* it will be replaced with "dog". If there are multiple matches, all
|
1643 |
+
* replacements will happen.
|
1644 |
+
*
|
1645 |
+
* @param array $terms An array of words.
|
1646 |
+
*
|
1647 |
+
* @return array An array of words with backwards synonym replacement.
|
1648 |
+
*/
|
1649 |
+
function relevanssi_replace_synonyms_in_terms( array $terms ) : array {
|
1650 |
+
$all_synonyms = get_option( 'relevanssi_synonyms', array() );
|
1651 |
+
$synonyms = explode( ';', $all_synonyms[ relevanssi_get_current_language() ] ?? '' );
|
1652 |
+
|
1653 |
+
return array_map(
|
1654 |
+
function ( $term ) use ( $synonyms ) {
|
1655 |
+
$new_term = array();
|
1656 |
+
foreach ( $synonyms as $pair ) {
|
1657 |
+
list( $key, $value ) = explode( '=', $pair );
|
1658 |
+
if ( $value === $term ) {
|
1659 |
+
$new_term[] = $key;
|
1660 |
+
}
|
1661 |
+
}
|
1662 |
+
if ( ! empty( $new_term ) ) {
|
1663 |
+
$term = implode( ' ', $new_term );
|
1664 |
+
}
|
1665 |
+
return $term;
|
1666 |
+
},
|
1667 |
+
$terms
|
1668 |
+
);
|
1669 |
+
}
|
lib/compatibility/ninjatables.php
CHANGED
@@ -77,7 +77,17 @@ function relevanssi_index_ninja_table( $table_id ) {
|
|
77 |
)
|
78 |
);
|
79 |
foreach ( $rows as $row ) {
|
80 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
81 |
}
|
82 |
|
83 |
return $table_contents;
|
77 |
)
|
78 |
);
|
79 |
foreach ( $rows as $row ) {
|
80 |
+
$array_values = array_map(
|
81 |
+
function( $value ) {
|
82 |
+
if ( is_object( $value ) ) {
|
83 |
+
return '';
|
84 |
+
}
|
85 |
+
return strval( $value );
|
86 |
+
},
|
87 |
+
array_values( get_object_vars( json_decode( $row->value ) ) )
|
88 |
+
);
|
89 |
+
|
90 |
+
$table_contents .= ' ' . implode( ' ', $array_values );
|
91 |
}
|
92 |
|
93 |
return $table_contents;
|
lib/compatibility/oxygen.php
CHANGED
@@ -13,6 +13,7 @@
|
|
13 |
add_filter( 'relevanssi_custom_field_value', 'relevanssi_oxygen_compatibility', 10, 3 );
|
14 |
add_filter( 'relevanssi_index_custom_fields', 'relevanssi_add_oxygen' );
|
15 |
add_filter( 'option_relevanssi_index_fields', 'relevanssi_oxygen_fix_none_setting' );
|
|
|
16 |
|
17 |
/**
|
18 |
* Cleans up the Oxygen Builder custom field for Relevanssi consumption.
|
@@ -140,3 +141,25 @@ function relevanssi_oxygen_fix_none_setting( $value ) {
|
|
140 |
|
141 |
return $value;
|
142 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
add_filter( 'relevanssi_custom_field_value', 'relevanssi_oxygen_compatibility', 10, 3 );
|
14 |
add_filter( 'relevanssi_index_custom_fields', 'relevanssi_add_oxygen' );
|
15 |
add_filter( 'option_relevanssi_index_fields', 'relevanssi_oxygen_fix_none_setting' );
|
16 |
+
add_filter( 'relevanssi_oxygen_section_content', 'relevanssi_oxygen_code_block' );
|
17 |
|
18 |
/**
|
19 |
* Cleans up the Oxygen Builder custom field for Relevanssi consumption.
|
141 |
|
142 |
return $value;
|
143 |
}
|
144 |
+
|
145 |
+
/**
|
146 |
+
* Indexes the Base64 encoded PHP & HTML code block contents.
|
147 |
+
*
|
148 |
+
* @param string $content The section content from the
|
149 |
+
* relevanssi_oxygen_section_content filter hook.
|
150 |
+
*
|
151 |
+
* @return string $content The content with the decoded code block content
|
152 |
+
* added to the end.
|
153 |
+
*/
|
154 |
+
function relevanssi_oxygen_code_block( $content ) {
|
155 |
+
if ( preg_match_all( '/\[ct_code_block.*?ct_code_block\]/', $content, $matches ) ) {
|
156 |
+
foreach ( $matches[0] as $match ) {
|
157 |
+
if ( preg_match_all( '/"code-php":"(.*?)"/', $match, $block_matches ) ) {
|
158 |
+
foreach ( $block_matches[1] as $encoded_text ) {
|
159 |
+
$content .= ' ' . base64_decode( $encoded_text ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions
|
160 |
+
}
|
161 |
+
}
|
162 |
+
}
|
163 |
+
}
|
164 |
+
return $content;
|
165 |
+
}
|
lib/excerpts-highlights.php
CHANGED
@@ -71,6 +71,16 @@ function relevanssi_do_excerpt( $t_post, $query, $excerpt_length = null, $excerp
|
|
71 |
} else {
|
72 |
$untokenized_terms = array_filter( explode( ' ', $query ) );
|
73 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
74 |
$untokenized_terms = array_flip(
|
75 |
relevanssi_remove_stopwords_from_array( $untokenized_terms )
|
76 |
);
|
@@ -338,7 +348,7 @@ function relevanssi_create_excerpt( $content, $terms, $query, $excerpt_length =
|
|
338 |
*
|
339 |
* @param string $content The content.
|
340 |
* @param array $terms The search terms, tokenized.
|
341 |
-
* @param string $query The search query
|
342 |
* @param int $excerpt_length The length of the excerpt, default 30.
|
343 |
* @param string $excerpt_type Either 'chars' or 'words', default 'words'.
|
344 |
*
|
@@ -348,7 +358,10 @@ function relevanssi_create_excerpt( $content, $terms, $query, $excerpt_length =
|
|
348 |
* content.
|
349 |
*/
|
350 |
function relevanssi_create_excerpts( $content, $terms, $query, $excerpt_length = 30, $excerpt_type = 'words' ) {
|
351 |
-
$content =
|
|
|
|
|
|
|
352 |
$content = html_entity_decode( $content );
|
353 |
// Finds all the phrases in the query.
|
354 |
$phrases = relevanssi_extract_phrases( stripslashes( $query ) );
|
@@ -384,7 +397,7 @@ function relevanssi_create_excerpts( $content, $terms, $query, $excerpt_length =
|
|
384 |
relevanssi_extract_relevant(
|
385 |
array_keys( $terms ),
|
386 |
$content,
|
387 |
-
$excerpt_length + 1,
|
388 |
$prev_count
|
389 |
);
|
390 |
$excerpt = array(
|
@@ -1436,3 +1449,56 @@ function relevanssi_excerpt_post_the_content() {
|
|
1436 |
remove_shortcode( 'noindex' );
|
1437 |
add_shortcode( 'noindex', 'relevanssi_noindex_shortcode' );
|
1438 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
71 |
} else {
|
72 |
$untokenized_terms = array_filter( explode( ' ', $query ) );
|
73 |
}
|
74 |
+
$untokenized_terms = array_map(
|
75 |
+
function( $term ) {
|
76 |
+
if ( is_numeric( $term ) ) {
|
77 |
+
$term = " $term";
|
78 |
+
}
|
79 |
+
return $term;
|
80 |
+
},
|
81 |
+
$untokenized_terms
|
82 |
+
);
|
83 |
+
|
84 |
$untokenized_terms = array_flip(
|
85 |
relevanssi_remove_stopwords_from_array( $untokenized_terms )
|
86 |
);
|
348 |
*
|
349 |
* @param string $content The content.
|
350 |
* @param array $terms The search terms, tokenized.
|
351 |
+
* @param string $query The search query.
|
352 |
* @param int $excerpt_length The length of the excerpt, default 30.
|
353 |
* @param string $excerpt_type Either 'chars' or 'words', default 'words'.
|
354 |
*
|
358 |
* content.
|
359 |
*/
|
360 |
function relevanssi_create_excerpts( $content, $terms, $query, $excerpt_length = 30, $excerpt_type = 'words' ) {
|
361 |
+
$content = preg_replace( '/\s+/u', ' ', $content );
|
362 |
+
if ( ' ' !== relevanssi_substr( $content, 0, 1 ) ) {
|
363 |
+
$content = ' ' . $content;
|
364 |
+
}
|
365 |
$content = html_entity_decode( $content );
|
366 |
// Finds all the phrases in the query.
|
367 |
$phrases = relevanssi_extract_phrases( stripslashes( $query ) );
|
397 |
relevanssi_extract_relevant(
|
398 |
array_keys( $terms ),
|
399 |
$content,
|
400 |
+
$excerpt_length + 1, // There's one space in the beginning of the content.
|
401 |
$prev_count
|
402 |
);
|
403 |
$excerpt = array(
|
1449 |
remove_shortcode( 'noindex' );
|
1450 |
add_shortcode( 'noindex', 'relevanssi_noindex_shortcode' );
|
1451 |
}
|
1452 |
+
|
1453 |
+
/**
|
1454 |
+
* Adds a highlighted title in the post object in $post->post_highlighted_title.
|
1455 |
+
*
|
1456 |
+
* @param WP_Post $post The post object (passed as reference).
|
1457 |
+
* @param string $query The search query.
|
1458 |
+
*
|
1459 |
+
* @uses relevanssi_highlight_terms
|
1460 |
+
*/
|
1461 |
+
function relevanssi_highlight_post_title( &$post, $query ) {
|
1462 |
+
$post->post_highlighted_title = wp_strip_all_tags( $post->post_title );
|
1463 |
+
$highlight = get_option( 'relevanssi_highlight' );
|
1464 |
+
if ( 'none' !== $highlight ) {
|
1465 |
+
if ( ! is_admin() || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
|
1466 |
+
$q_for_highlight = 'on' === get_option( 'relevanssi_index_synonyms', 'off' )
|
1467 |
+
? relevanssi_add_synonyms( $query )
|
1468 |
+
: $query;
|
1469 |
+
|
1470 |
+
$post->post_highlighted_title = relevanssi_highlight_terms(
|
1471 |
+
$post->post_highlighted_title,
|
1472 |
+
$q_for_highlight
|
1473 |
+
);
|
1474 |
+
}
|
1475 |
+
}
|
1476 |
+
}
|
1477 |
+
|
1478 |
+
/**
|
1479 |
+
* Replaces $post->post_excerpt with the Relevanssi-generated excerpt and puts
|
1480 |
+
* the original excerpt in $post->original_excerpt.
|
1481 |
+
*
|
1482 |
+
* @param WP_Post $post The post object (passed as reference).
|
1483 |
+
* @param string $query The search query.
|
1484 |
+
*
|
1485 |
+
* @uses relevanssi_do_excerpt
|
1486 |
+
*/
|
1487 |
+
function relevanssi_add_excerpt( &$post, $query ) {
|
1488 |
+
if ( isset( $post->blog_id ) ) {
|
1489 |
+
switch_to_blog( $post->blog_id );
|
1490 |
+
}
|
1491 |
+
$excerpt_length = get_option( 'relevanssi_excerpt_length' );
|
1492 |
+
$excerpt_type = get_option( 'relevanssi_excerpt_type' );
|
1493 |
+
$post->original_excerpt = $post->post_excerpt;
|
1494 |
+
$post->post_excerpt = relevanssi_do_excerpt(
|
1495 |
+
$post,
|
1496 |
+
$query,
|
1497 |
+
$excerpt_length,
|
1498 |
+
$excerpt_type
|
1499 |
+
);
|
1500 |
+
|
1501 |
+
if ( isset( $post->blog_id ) ) {
|
1502 |
+
restore_current_blog();
|
1503 |
+
}
|
1504 |
+
}
|
lib/indexing.php
CHANGED
@@ -542,7 +542,7 @@ function relevanssi_index_doc( $index_post, $remove_first = false, $custom_field
|
|
542 |
}
|
543 |
|
544 |
if ( 'on' === get_option( 'relevanssi_index_author' ) ) {
|
545 |
-
$n += relevanssi_index_author( $insert_data, $post->post_author, $min_word_length, $debug );
|
546 |
}
|
547 |
|
548 |
$n += relevanssi_index_custom_fields( $insert_data, $post->ID, $custom_fields, $min_word_length, $debug );
|
@@ -1179,12 +1179,22 @@ function relevanssi_index_comments( &$insert_data, $post_id, $min_word_length, $
|
|
1179 |
* @param int $post_author The post author id.
|
1180 |
* @param int $min_word_length The minimum word length.
|
1181 |
* @param boolean $debug If true, print out debug notices.
|
|
|
1182 |
*
|
1183 |
* @return int The number of tokens added to the data.
|
1184 |
*/
|
1185 |
-
function relevanssi_index_author( &$insert_data, $post_author, $min_word_length, $debug ) {
|
1186 |
$n = 0;
|
1187 |
$display_name = get_the_author_meta( 'display_name', $post_author );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1188 |
/** This filter is documented in lib/indexing.php */
|
1189 |
$name_tokens = apply_filters(
|
1190 |
'relevanssi_indexing_tokens',
|
542 |
}
|
543 |
|
544 |
if ( 'on' === get_option( 'relevanssi_index_author' ) ) {
|
545 |
+
$n += relevanssi_index_author( $insert_data, $post->post_author, $min_word_length, $debug, $post );
|
546 |
}
|
547 |
|
548 |
$n += relevanssi_index_custom_fields( $insert_data, $post->ID, $custom_fields, $min_word_length, $debug );
|
1179 |
* @param int $post_author The post author id.
|
1180 |
* @param int $min_word_length The minimum word length.
|
1181 |
* @param boolean $debug If true, print out debug notices.
|
1182 |
+
* @param WP_Post $post The post object.
|
1183 |
*
|
1184 |
* @return int The number of tokens added to the data.
|
1185 |
*/
|
1186 |
+
function relevanssi_index_author( &$insert_data, $post_author, $min_word_length, $debug, $post ) {
|
1187 |
$n = 0;
|
1188 |
$display_name = get_the_author_meta( 'display_name', $post_author );
|
1189 |
+
|
1190 |
+
/**
|
1191 |
+
* Filters the post author display_name before indexing it.
|
1192 |
+
*
|
1193 |
+
* @param string $post_author The author display_name.
|
1194 |
+
* @param WP_Post $post The post object.
|
1195 |
+
*/
|
1196 |
+
$display_name = apply_filters( 'relevanssi_post_author', $display_name, $post );
|
1197 |
+
|
1198 |
/** This filter is documented in lib/indexing.php */
|
1199 |
$name_tokens = apply_filters(
|
1200 |
'relevanssi_indexing_tokens',
|
lib/init.php
CHANGED
@@ -22,6 +22,7 @@ add_filter( 'posts_request', 'relevanssi_prevent_default_request', 10, 2 );
|
|
22 |
add_filter( 'relevanssi_search_ok', 'relevanssi_block_on_admin_searches', 10, 2 );
|
23 |
add_filter( 'relevanssi_admin_search_ok', 'relevanssi_block_on_admin_searches', 10, 2 );
|
24 |
add_filter( 'relevanssi_prevent_default_request', 'relevanssi_block_on_admin_searches', 10, 2 );
|
|
|
25 |
|
26 |
// Post indexing.
|
27 |
add_action( 'wp_insert_post', 'relevanssi_insert_edit', 99, 1 );
|
22 |
add_filter( 'relevanssi_search_ok', 'relevanssi_block_on_admin_searches', 10, 2 );
|
23 |
add_filter( 'relevanssi_admin_search_ok', 'relevanssi_block_on_admin_searches', 10, 2 );
|
24 |
add_filter( 'relevanssi_prevent_default_request', 'relevanssi_block_on_admin_searches', 10, 2 );
|
25 |
+
add_filter( 'relevanssi_search_ok', 'relevanssi_control_media_queries', 11, 2 );
|
26 |
|
27 |
// Post indexing.
|
28 |
add_action( 'wp_insert_post', 'relevanssi_insert_edit', 99, 1 );
|
lib/phrases.php
CHANGED
@@ -179,7 +179,17 @@ $custom_fields, string $excerpts ) : array {
|
|
179 |
$phrase = $wpdb->esc_like( $phrase );
|
180 |
$phrase = str_replace( array( '‘', '’', "'", '"', '”', '“', '“', '„', '´' ), '_', $phrase );
|
181 |
$phrase = htmlspecialchars( $phrase );
|
182 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
183 |
|
184 |
$excerpt = '';
|
185 |
if ( 'on' === $excerpts ) {
|
179 |
$phrase = $wpdb->esc_like( $phrase );
|
180 |
$phrase = str_replace( array( '‘', '’', "'", '"', '”', '“', '“', '„', '´' ), '_', $phrase );
|
181 |
$phrase = htmlspecialchars( $phrase );
|
182 |
+
|
183 |
+
/**
|
184 |
+
* Filters each phrase before it's passed through esc_sql() and used in
|
185 |
+
* the MySQL query. You can use this filter hook to for example run
|
186 |
+
* htmlentities() on the phrase in case your database needs that.
|
187 |
+
*
|
188 |
+
* @param string $phrase The phrase after quotes are replaced with a
|
189 |
+
* MySQL wild card and the phrase has been passed through esc_like() and
|
190 |
+
* htmlspecialchars().
|
191 |
+
*/
|
192 |
+
$phrase = esc_sql( apply_filters( 'relevanssi_phrase', $phrase ) );
|
193 |
|
194 |
$excerpt = '';
|
195 |
if ( 'on' === $excerpts ) {
|
lib/search.php
CHANGED
@@ -50,16 +50,6 @@ function relevanssi_query( $posts, $query = false ) {
|
|
50 |
$search_ok = false; // No search term.
|
51 |
}
|
52 |
|
53 |
-
$indexed_post_types = array_flip(
|
54 |
-
get_option( 'relevanssi_index_post_types', array() )
|
55 |
-
);
|
56 |
-
$images_indexed = get_option( 'relevanssi_index_image_files', 'off' );
|
57 |
-
if ( $search_ok && ( false === isset( $indexed_post_types['attachment'] ) || 'off' === $images_indexed ) ) {
|
58 |
-
if ( 'attachment' === $query->query_vars['post_type'] && 'inherit,private' === $query->query_vars['post_status'] ) {
|
59 |
-
$search_ok = false;
|
60 |
-
}
|
61 |
-
}
|
62 |
-
|
63 |
if ( $query->get( 'relevanssi' ) ) {
|
64 |
$search_ok = true; // Manual override, always search.
|
65 |
}
|
@@ -114,8 +104,7 @@ function relevanssi_query( $posts, $query = false ) {
|
|
114 |
* @return array An array of return values.
|
115 |
*/
|
116 |
function relevanssi_search( $args ) {
|
117 |
-
global $wpdb
|
118 |
-
$relevanssi_table = $relevanssi_variables['relevanssi_table'];
|
119 |
|
120 |
/**
|
121 |
* Filters the search parameters.
|
@@ -150,6 +139,9 @@ function relevanssi_search( $args ) {
|
|
150 |
$terms = relevanssi_tokenize( $q, $remove_stopwords, $min_length );
|
151 |
$terms = array_keys( $terms ); // Don't care about tf in query.
|
152 |
|
|
|
|
|
|
|
153 |
if ( function_exists( 'relevanssi_process_terms' ) ) {
|
154 |
$process_terms_results = relevanssi_process_terms( $terms, $q );
|
155 |
$query_restrictions .= $process_terms_results['query_restrictions'];
|
@@ -192,139 +184,31 @@ function relevanssi_search( $args ) {
|
|
192 |
}
|
193 |
}
|
194 |
|
195 |
-
$
|
196 |
-
|
197 |
-
$title_matches = array();
|
198 |
-
$tag_matches = array();
|
199 |
-
$comment_matches = array();
|
200 |
-
$link_matches = array();
|
201 |
-
$body_matches = array();
|
202 |
-
$category_matches = array();
|
203 |
-
$taxonomy_matches = array();
|
204 |
-
$customfield_matches = array();
|
205 |
-
$mysqlcolumn_matches = array();
|
206 |
-
$author_matches = array();
|
207 |
-
$excerpt_matches = array();
|
208 |
$term_hits = array();
|
209 |
-
|
210 |
-
$fuzzy = get_option( 'relevanssi_fuzzy' );
|
211 |
-
|
212 |
-
$no_matches = true;
|
213 |
-
|
214 |
-
$post_type_weights = get_option( 'relevanssi_post_type_weights' );
|
215 |
-
|
216 |
-
$recency_bonus = false;
|
217 |
-
$recency_cutoff_date = false;
|
218 |
-
if ( function_exists( 'relevanssi_get_recency_bonus' ) ) {
|
219 |
-
$recency_details = relevanssi_get_recency_bonus();
|
220 |
-
$recency_bonus = $recency_details['bonus'];
|
221 |
-
$recency_cutoff_date = $recency_details['cutoff'];
|
222 |
-
}
|
223 |
-
|
224 |
-
$exact_match_bonus = false;
|
225 |
-
if ( 'on' === get_option( 'relevanssi_exact_match_bonus' ) ) {
|
226 |
-
$exact_match_bonus = true;
|
227 |
-
/**
|
228 |
-
* Filters the exact match bonus.
|
229 |
-
*
|
230 |
-
* @param array The title bonus under 'title' (default 5) and the content
|
231 |
-
* bonus under 'content' (default 2).
|
232 |
-
*/
|
233 |
-
$exact_match_boost = apply_filters(
|
234 |
-
'relevanssi_exact_match_bonus',
|
235 |
-
array(
|
236 |
-
'title' => 5,
|
237 |
-
'content' => 2,
|
238 |
-
)
|
239 |
-
);
|
240 |
-
|
241 |
-
}
|
242 |
-
|
243 |
-
$search_again = false;
|
244 |
-
|
245 |
-
$content_boost = floatval( get_option( 'relevanssi_content_boost', 1 ) ); // Default value, because this option was added late.
|
246 |
-
$title_boost = floatval( get_option( 'relevanssi_title_boost' ) );
|
247 |
-
$link_boost = floatval( get_option( 'relevanssi_link_boost' ) );
|
248 |
-
$comment_boost = floatval( get_option( 'relevanssi_comment_boost' ) );
|
249 |
-
|
250 |
-
$tag = $relevanssi_variables['post_type_weight_defaults']['post_tag'];
|
251 |
-
$cat = $relevanssi_variables['post_type_weight_defaults']['category'];
|
252 |
-
|
253 |
-
if ( ! empty( $post_type_weights['post_tagged_with_post_tag'] ) ) {
|
254 |
-
$tag = $post_type_weights['post_tagged_with_post_tag'];
|
255 |
-
}
|
256 |
-
if ( ! empty( $post_type_weights['post_tagged_with_category'] ) ) {
|
257 |
-
$cat = $post_type_weights['post_tagged_with_category'];
|
258 |
-
}
|
259 |
-
|
260 |
-
// Legacy code, improvement introduced in 2.1.8, remove at some point.
|
261 |
-
// phpcs:ignore Squiz.Commenting.InlineComment
|
262 |
-
// @codeCoverageIgnoreStart
|
263 |
-
if ( ! empty( $post_type_weights['post_tag'] ) ) {
|
264 |
-
$tag = $post_type_weights['post_tag'];
|
265 |
-
}
|
266 |
-
if ( ! empty( $post_type_weights['category'] ) ) {
|
267 |
-
$cat = $post_type_weights['category'];
|
268 |
-
}
|
269 |
-
// @codeCoverageIgnoreEnd
|
270 |
-
/* End legacy code. */
|
271 |
-
|
272 |
$include_these_posts = array();
|
273 |
$include_these_items = array();
|
274 |
-
$df_counts = array();
|
275 |
$doc_weight = array();
|
|
|
|
|
|
|
|
|
|
|
276 |
|
277 |
do {
|
278 |
-
|
279 |
-
$
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
$query_restrictions,
|
286 |
-
$
|
287 |
-
|
288 |
-
|
289 |
-
);
|
290 |
-
|
291 |
-
$query = "SELECT COUNT(DISTINCT(relevanssi.doc)) FROM $relevanssi_table AS relevanssi
|
292 |
-
$query_join WHERE $term_cond $this_query_restrictions";
|
293 |
-
// Clean: $this_query_restrictions is escaped, $term_cond is escaped.
|
294 |
-
/**
|
295 |
-
* Filters the DF query.
|
296 |
-
*
|
297 |
-
* This query is used to calculate the df for the tf * idf calculations.
|
298 |
-
*
|
299 |
-
* @param string MySQL query to filter.
|
300 |
-
*/
|
301 |
-
$query = apply_filters( 'relevanssi_df_query_filter', $query );
|
302 |
-
|
303 |
-
$df = $wpdb->get_var( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
304 |
-
|
305 |
-
if ( $df < 1 && 'sometimes' === $fuzzy ) {
|
306 |
-
$term_cond = relevanssi_generate_term_where( $term, true, $no_terms );
|
307 |
-
$query = "
|
308 |
-
SELECT COUNT(DISTINCT(relevanssi.doc))
|
309 |
-
FROM $relevanssi_table AS relevanssi
|
310 |
-
$query_join WHERE $term_cond $query_restrictions";
|
311 |
-
// Clean: $query_restrictions is escaped, $term is escaped.
|
312 |
-
/** Documented in lib/search.php. */
|
313 |
-
$query = apply_filters( 'relevanssi_df_query_filter', $query );
|
314 |
-
$df = $wpdb->get_var( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
315 |
-
}
|
316 |
-
|
317 |
-
$df_counts[ $term ] = $df;
|
318 |
-
}
|
319 |
-
|
320 |
-
// Sort the terms in ascending DF order, so that rarest terms are searched
|
321 |
-
// for first. This is to make sure the throttle doesn't cut off posts with
|
322 |
-
// rare search terms.
|
323 |
-
asort( $df_counts );
|
324 |
|
325 |
foreach ( $df_counts as $term => $df ) {
|
326 |
-
$term_cond = relevanssi_generate_term_where( $term, $search_again, $no_terms );
|
327 |
-
|
328 |
$this_query_restrictions = relevanssi_add_phrase_restrictions(
|
329 |
$query_restrictions,
|
330 |
$phrase_queries,
|
@@ -332,84 +216,27 @@ function relevanssi_search( $args ) {
|
|
332 |
$operator
|
333 |
);
|
334 |
|
335 |
-
$query
|
336 |
-
relevanssi.content * $content_boost + relevanssi.comment * $comment_boost +
|
337 |
-
relevanssi.tag * $tag + relevanssi.link * $link_boost +
|
338 |
-
relevanssi.author + relevanssi.category * $cat + relevanssi.excerpt +
|
339 |
-
relevanssi.taxonomy + relevanssi.customfield + relevanssi.mysqlcolumn AS tf
|
340 |
-
FROM $relevanssi_table AS relevanssi $query_join WHERE $term_cond $this_query_restrictions";
|
341 |
-
/** Clean: $this_query_restrictions is escaped, $term_cond is escaped. */
|
342 |
-
|
343 |
-
/**
|
344 |
-
* Filters the Relevanssi MySQL query.
|
345 |
-
*
|
346 |
-
* The last chance to filter the MySQL query before it is run.
|
347 |
-
*
|
348 |
-
* @param string MySQL query for the Relevanssi search.
|
349 |
-
*/
|
350 |
-
$query = apply_filters( 'relevanssi_query_filter', $query );
|
351 |
$matches = $wpdb->get_results( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
|
|
352 |
if ( count( $matches ) < 1 ) {
|
353 |
continue;
|
354 |
-
} else {
|
355 |
-
$no_matches = false;
|
356 |
-
if ( count( $include_these_posts ) > 0 ) {
|
357 |
-
$existing_ids = array();
|
358 |
-
foreach ( $matches as $match ) {
|
359 |
-
$existing_ids[] = $match->doc;
|
360 |
-
}
|
361 |
-
$existing_ids = array_keys( array_flip( $existing_ids ) );
|
362 |
-
$added_post_ids = array_diff( array_keys( $include_these_posts ), $existing_ids );
|
363 |
-
if ( count( $added_post_ids ) > 0 ) {
|
364 |
-
$offset = 0;
|
365 |
-
$slice_length = 20;
|
366 |
-
$total_ids = count( $added_post_ids );
|
367 |
-
do {
|
368 |
-
$current_slice = array_slice( $added_post_ids, $offset, $slice_length );
|
369 |
-
$post_ids_to_add = implode( ',', $current_slice );
|
370 |
-
if ( ! empty( $post_ids_to_add ) ) {
|
371 |
-
$query = "SELECT relevanssi.*, relevanssi.title * $title_boost +
|
372 |
-
relevanssi.content * $content_boost + relevanssi.comment * $comment_boost +
|
373 |
-
relevanssi.tag * $tag + relevanssi.link * $link_boost +
|
374 |
-
relevanssi.author + relevanssi.category * $cat + relevanssi.excerpt +
|
375 |
-
relevanssi.taxonomy + relevanssi.customfield + relevanssi.mysqlcolumn AS tf
|
376 |
-
FROM $relevanssi_table AS relevanssi WHERE relevanssi.doc IN ($post_ids_to_add)
|
377 |
-
AND $term_cond";
|
378 |
-
|
379 |
-
// Clean: no unescaped user inputs.
|
380 |
-
$matches_to_add = $wpdb->get_results( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
381 |
-
$matches = array_merge( $matches, $matches_to_add );
|
382 |
-
}
|
383 |
-
$offset += $slice_length;
|
384 |
-
} while ( $offset <= $total_ids );
|
385 |
-
}
|
386 |
-
}
|
387 |
-
if ( count( $include_these_items ) > 0 ) {
|
388 |
-
$existing_items = array();
|
389 |
-
foreach ( $matches as $match ) {
|
390 |
-
if ( 0 !== intval( $match->item ) ) {
|
391 |
-
$existing_items[] = $match->item;
|
392 |
-
}
|
393 |
-
}
|
394 |
-
$existing_items = array_keys( array_flip( $existing_items ) );
|
395 |
-
$items_to_add = implode( ',', array_diff( array_keys( $include_these_items ), $existing_items ) );
|
396 |
-
|
397 |
-
if ( ! empty( $items_to_add ) ) {
|
398 |
-
$query = "SELECT relevanssi.*, relevanssi.title * $title_boost +
|
399 |
-
relevanssi.content * $content_boost + relevanssi.comment * $comment_boost +
|
400 |
-
relevanssi.tag * $tag + relevanssi.link * $link_boost +
|
401 |
-
relevanssi.author + relevanssi.category * $cat + relevanssi.excerpt +
|
402 |
-
relevanssi.taxonomy + relevanssi.customfield + relevanssi.mysqlcolumn AS tf
|
403 |
-
FROM $relevanssi_table AS relevanssi WHERE relevanssi.item IN ($items_to_add)
|
404 |
-
AND $term_cond";
|
405 |
-
|
406 |
-
// Clean: no unescaped user inputs.
|
407 |
-
$matches_to_add = $wpdb->get_results( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
408 |
-
$matches = array_merge( $matches, $matches_to_add );
|
409 |
-
}
|
410 |
-
}
|
411 |
}
|
412 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
413 |
relevanssi_populate_array( $matches );
|
414 |
|
415 |
$total_hits += count( $matches );
|
@@ -421,162 +248,31 @@ function relevanssi_search( $args ) {
|
|
421 |
}
|
422 |
|
423 |
foreach ( $matches as $match ) {
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
$match->doc = 'p_' . $match->item;
|
428 |
-
} elseif ( ! in_array( $match->type, array( 'post', 'attachment' ), true ) ) {
|
429 |
-
$match->doc = '**' . $match->type . '**' . $match->item;
|
430 |
-
}
|
431 |
-
|
432 |
-
if ( ! empty( $match->taxonomy_detail ) ) {
|
433 |
-
relevanssi_taxonomy_score( $match, $post_type_weights );
|
434 |
-
} else {
|
435 |
-
$tag_weight = 1;
|
436 |
-
if ( isset( $post_type_weights['post_tagged_with_post_tag'] ) && is_numeric( $post_type_weights['post_tagged_with_post_tag'] ) ) {
|
437 |
-
$tag_weight = $post_type_weights['post_tagged_with_post_tag'];
|
438 |
-
}
|
439 |
-
|
440 |
-
$category_weight = 1;
|
441 |
-
if ( isset( $post_type_weights['post_tagged_with_category'] ) && is_numeric( $post_type_weights['post_tagged_with_category'] ) ) {
|
442 |
-
$category_weight = $post_type_weights['post_tagged_with_category'];
|
443 |
-
}
|
444 |
-
|
445 |
-
// Legacy code from 2.1.8. Remove at some point.
|
446 |
-
// phpcs:ignore Squiz.Commenting.InlineComment
|
447 |
-
// @codeCoverageIgnoreStart
|
448 |
-
if ( isset( $post_type_weights['post_tag'] ) && is_numeric( $post_type_weights['post_tag'] ) ) {
|
449 |
-
$tag_weight = $post_type_weights['post_tag'];
|
450 |
-
}
|
451 |
-
|
452 |
-
$category_weight = 1;
|
453 |
-
if ( isset( $post_type_weights['category'] ) && is_numeric( $post_type_weights['category'] ) ) {
|
454 |
-
$category_weight = $post_type_weights['category'];
|
455 |
-
}
|
456 |
-
// @codeCoverageIgnoreEnd
|
457 |
-
/* End legacy code. */
|
458 |
-
|
459 |
-
$taxonomy_weight = 1;
|
460 |
-
|
461 |
-
$match->taxonomy_score =
|
462 |
-
$match->tag * $tag_weight +
|
463 |
-
$match->category * $category_weight +
|
464 |
-
$match->taxonomy * $taxonomy_weight;
|
465 |
-
}
|
466 |
-
|
467 |
-
$match->tf =
|
468 |
-
$match->title * $title_boost +
|
469 |
-
$match->content * $content_boost +
|
470 |
-
$match->comment * $comment_boost +
|
471 |
-
$match->link * $link_boost +
|
472 |
-
$match->author +
|
473 |
-
$match->excerpt +
|
474 |
-
$match->taxonomy_score +
|
475 |
-
$match->customfield +
|
476 |
-
$match->mysqlcolumn;
|
477 |
-
|
478 |
-
$term_hits[ $match->doc ][ $term ] =
|
479 |
-
$match->title +
|
480 |
-
$match->content +
|
481 |
-
$match->comment +
|
482 |
-
$match->tag +
|
483 |
-
$match->link +
|
484 |
-
$match->author +
|
485 |
-
$match->category +
|
486 |
-
$match->excerpt +
|
487 |
-
$match->taxonomy +
|
488 |
-
$match->customfield +
|
489 |
-
$match->mysqlcolumn;
|
490 |
-
|
491 |
-
$match->weight = $match->tf * $idf;
|
492 |
-
|
493 |
-
if ( $recency_bonus ) {
|
494 |
-
$post = relevanssi_get_post( $match->doc );
|
495 |
-
if ( strtotime( $post->post_date ) > $recency_cutoff_date ) {
|
496 |
-
$match->weight = $match->weight * $recency_bonus;
|
497 |
-
}
|
498 |
-
}
|
499 |
-
|
500 |
-
if ( $exact_match_bonus ) {
|
501 |
-
$post = relevanssi_get_post( $match->doc );
|
502 |
-
$clean_q = relevanssi_remove_quotes( $q_no_synonyms );
|
503 |
-
if ( $post && $clean_q ) {
|
504 |
-
if ( stristr( $post->post_title, $clean_q ) !== false ) {
|
505 |
-
$match->weight *= $exact_match_boost['title'];
|
506 |
-
}
|
507 |
-
if ( stristr( $post->post_content, $clean_q ) !== false ) {
|
508 |
-
$match->weight *= $exact_match_boost['content'];
|
509 |
-
}
|
510 |
-
}
|
511 |
-
}
|
512 |
-
|
513 |
-
if ( ! isset( $body_matches[ $match->doc ] ) ) {
|
514 |
-
$body_matches[ $match->doc ] = 0;
|
515 |
-
}
|
516 |
-
if ( ! isset( $title_matches[ $match->doc ] ) ) {
|
517 |
-
$title_matches[ $match->doc ] = 0;
|
518 |
-
}
|
519 |
-
if ( ! isset( $link_matches[ $match->doc ] ) ) {
|
520 |
-
$link_matches[ $match->doc ] = 0;
|
521 |
-
}
|
522 |
-
if ( ! isset( $tag_matches[ $match->doc ] ) ) {
|
523 |
-
$tag_matches[ $match->doc ] = 0;
|
524 |
-
}
|
525 |
-
if ( ! isset( $category_matches[ $match->doc ] ) ) {
|
526 |
-
$category_matches[ $match->doc ] = 0;
|
527 |
-
}
|
528 |
-
if ( ! isset( $taxonomy_matches[ $match->doc ] ) ) {
|
529 |
-
$taxonomy_matches[ $match->doc ] = 0;
|
530 |
-
}
|
531 |
-
if ( ! isset( $comment_matches[ $match->doc ] ) ) {
|
532 |
-
$comment_matches[ $match->doc ] = 0;
|
533 |
-
}
|
534 |
-
if ( ! isset( $customfield_matches[ $match->doc ] ) ) {
|
535 |
-
$customfield_matches[ $match->doc ] = 0;
|
536 |
-
}
|
537 |
-
if ( ! isset( $author_matches[ $match->doc ] ) ) {
|
538 |
-
$author_matches[ $match->doc ] = 0;
|
539 |
-
}
|
540 |
-
if ( ! isset( $excerpt_matches[ $match->doc ] ) ) {
|
541 |
-
$excerpt_matches[ $match->doc ] = 0;
|
542 |
-
}
|
543 |
-
if ( ! isset( $mysqlcolumn_matches[ $match->doc ] ) ) {
|
544 |
-
$mysqlcolumn_matches[ $match->doc ] = 0;
|
545 |
-
}
|
546 |
-
$body_matches[ $match->doc ] += $match->content;
|
547 |
-
$title_matches[ $match->doc ] += $match->title;
|
548 |
-
$link_matches[ $match->doc ] += $match->link;
|
549 |
-
$tag_matches[ $match->doc ] += $match->tag;
|
550 |
-
$category_matches[ $match->doc ] += $match->category;
|
551 |
-
$taxonomy_matches[ $match->doc ] += $match->taxonomy;
|
552 |
-
$comment_matches[ $match->doc ] += $match->comment;
|
553 |
-
$customfield_matches[ $match->doc ] += $match->customfield;
|
554 |
-
$author_matches[ $match->doc ] += $match->author;
|
555 |
-
$excerpt_matches[ $match->doc ] += $match->excerpt;
|
556 |
-
$mysqlcolumn_matches[ $match->doc ] += $match->mysqlcolumn;
|
557 |
-
|
558 |
-
/* Post type weights. */
|
559 |
-
$type = relevanssi_get_post_type( $match->doc );
|
560 |
-
if ( ! empty( $post_type_weights[ $type ] ) ) {
|
561 |
-
$match->weight = $match->weight * $post_type_weights[ $type ];
|
562 |
-
}
|
563 |
-
|
564 |
-
/* Weight boost for taxonomy terms based on taxonomy. */
|
565 |
-
if ( ! empty( $post_type_weights[ 'taxonomy_term_' . $match->type ] ) ) {
|
566 |
-
$match->weight = $match->weight * $post_type_weights[ 'taxonomy_term_' . $match->type ];
|
567 |
-
}
|
568 |
|
569 |
/**
|
570 |
-
* Filters the
|
|
|
|
|
|
|
|
|
|
|
571 |
*
|
572 |
-
*
|
573 |
-
*
|
574 |
-
*
|
|
|
|
|
|
|
|
|
575 |
*
|
576 |
-
* @param object $match The match object
|
577 |
-
*
|
578 |
-
*
|
579 |
-
*
|
|
|
580 |
*/
|
581 |
$match = apply_filters( 'relevanssi_match', $match, $idf, $term );
|
582 |
if ( $match->weight <= 0 ) {
|
@@ -597,6 +293,8 @@ function relevanssi_search( $args ) {
|
|
597 |
*/
|
598 |
$post_ok = apply_filters( 'relevanssi_post_ok', $post_ok, $match->doc );
|
599 |
if ( $post_ok ) {
|
|
|
|
|
600 |
$doc_terms[ $match->doc ][ $term ] = true; // Count how many terms are matched to a doc.
|
601 |
if ( ! isset( $doc_weight[ $match->doc ] ) ) {
|
602 |
$doc_weight[ $match->doc ] = 0;
|
@@ -635,9 +333,9 @@ function relevanssi_search( $args ) {
|
|
635 |
/**
|
636 |
* Filters the parameters for fallback search.
|
637 |
*
|
638 |
-
* If you want to make Relevanssi search again with different
|
639 |
-
* can use this filter hook to adjust the parameters.
|
640 |
-
* $params['search_again'] to true to make Relevanssi do a new search.
|
641 |
*
|
642 |
* @param array The search parameters.
|
643 |
*/
|
@@ -658,6 +356,14 @@ function relevanssi_search( $args ) {
|
|
658 |
}
|
659 |
$total_terms = count( $terms_without_stops );
|
660 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
661 |
if ( isset( $doc_weight ) ) {
|
662 |
/**
|
663 |
* Filters the results Relevanssi finds.
|
@@ -670,6 +376,8 @@ function relevanssi_search( $args ) {
|
|
670 |
$doc_weight = apply_filters( 'relevanssi_results', $doc_weight );
|
671 |
}
|
672 |
|
|
|
|
|
673 |
if ( isset( $doc_weight ) && count( $doc_weight ) > 0 ) {
|
674 |
arsort( $doc_weight );
|
675 |
$i = 0;
|
@@ -679,21 +387,36 @@ function relevanssi_search( $args ) {
|
|
679 |
// doc didn't match all terms, so it's discarded.
|
680 |
continue;
|
681 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
682 |
|
683 |
if ( ! empty( $fields ) ) {
|
684 |
if ( 'ids' === $fields ) {
|
685 |
$hits[ intval( $i ) ] = $doc;
|
686 |
}
|
687 |
if ( 'id=>parent' === $fields ) {
|
688 |
-
$
|
689 |
-
|
690 |
-
|
691 |
-
|
692 |
-
$hits[ intval( $i ) ] = $object;
|
693 |
}
|
694 |
} else {
|
695 |
$hits[ intval( $i ) ] = relevanssi_get_post( $doc );
|
696 |
$hits[ intval( $i ) ]->relevance_score = round( $weight, 2 );
|
|
|
|
|
|
|
|
|
697 |
}
|
698 |
$i++;
|
699 |
}
|
@@ -710,21 +433,21 @@ function relevanssi_search( $args ) {
|
|
710 |
$or_args['q'] = relevanssi_add_synonyms( $q );
|
711 |
$return = relevanssi_search( $or_args );
|
712 |
|
713 |
-
$hits
|
714 |
-
$
|
715 |
-
$
|
716 |
-
$
|
717 |
-
$
|
718 |
-
$
|
719 |
-
$
|
720 |
-
$
|
721 |
-
$
|
722 |
-
$
|
723 |
-
$
|
724 |
-
$
|
725 |
-
$term_hits
|
726 |
-
$doc_weight
|
727 |
-
$q
|
728 |
}
|
729 |
$params = array( 'args' => $args );
|
730 |
/**
|
@@ -739,84 +462,45 @@ function relevanssi_search( $args ) {
|
|
739 |
$params = apply_filters( 'relevanssi_fallback', $params );
|
740 |
$args = $params['args'];
|
741 |
if ( isset( $params['return'] ) ) {
|
742 |
-
$return
|
743 |
-
$hits
|
744 |
-
$
|
745 |
-
$
|
746 |
-
$
|
747 |
-
$
|
748 |
-
$
|
749 |
-
$
|
750 |
-
$
|
751 |
-
$
|
752 |
-
$
|
753 |
-
$
|
754 |
-
$
|
755 |
-
$term_hits
|
756 |
-
$doc_weight
|
757 |
-
$q
|
758 |
}
|
759 |
}
|
760 |
|
761 |
-
$
|
762 |
-
if ( empty( $orderby ) ) {
|
763 |
-
$orderby = $default_order;
|
764 |
-
}
|
765 |
-
if ( is_array( $orderby ) ) {
|
766 |
-
/**
|
767 |
-
* Filters the 'orderby' value just before sorting.
|
768 |
-
*
|
769 |
-
* Relevanssi can use both array orderby ie. array( orderby => order )
|
770 |
-
* with multiple orderby parameters, or a single pair of orderby and
|
771 |
-
* order parameters. To avoid problems, try sticking to one and don't
|
772 |
-
* use this filter to make surprising changes between different formats.
|
773 |
-
*
|
774 |
-
* @param string The 'orderby' parameter.
|
775 |
-
*/
|
776 |
-
$orderby = apply_filters( 'relevanssi_orderby', $orderby );
|
777 |
-
relevanssi_object_sort( $hits, $orderby, $meta_query );
|
778 |
-
} else {
|
779 |
-
if ( empty( $order ) ) {
|
780 |
-
$order = 'desc';
|
781 |
-
}
|
782 |
-
$order = strtolower( $order );
|
783 |
-
|
784 |
-
$order_accepted_values = array( 'asc', 'desc' );
|
785 |
-
if ( ! in_array( $order, $order_accepted_values, true ) ) {
|
786 |
-
$order = 'desc';
|
787 |
-
}
|
788 |
-
|
789 |
-
/** Documented in lib/search.php. */
|
790 |
-
$orderby = apply_filters( 'relevanssi_orderby', $orderby );
|
791 |
-
/**
|
792 |
-
* Filters the 'order' value just before sorting.
|
793 |
-
*
|
794 |
-
* @param string The 'order' parameter.
|
795 |
-
*/
|
796 |
-
$order = apply_filters( 'relevanssi_order', $order );
|
797 |
|
798 |
-
if ( 'relevance' !== $orderby ) {
|
799 |
-
$orderby_array = array( $orderby => $order );
|
800 |
-
relevanssi_object_sort( $hits, $orderby_array, $meta_query );
|
801 |
-
}
|
802 |
-
}
|
803 |
$return = array(
|
804 |
'hits' => $hits,
|
805 |
-
'body_matches' => $
|
806 |
-
'title_matches' => $
|
807 |
-
'tag_matches' => $
|
808 |
-
'category_matches' => $
|
809 |
-
'comment_matches' => $
|
810 |
-
'taxonomy_matches' => $
|
811 |
-
'link_matches' => $
|
812 |
-
'customfield_matches' => $
|
813 |
-
'mysqlcolumn_matches' => $
|
814 |
-
'author_matches' => $
|
815 |
-
'excerpt_matches' => $
|
816 |
'term_hits' => $term_hits,
|
817 |
'query' => $q,
|
818 |
'doc_weights' => $doc_weight,
|
819 |
'query_no_synonyms' => $q_no_synonyms,
|
|
|
820 |
);
|
821 |
|
822 |
return $return;
|
@@ -928,91 +612,40 @@ function relevanssi_do_query( &$query ) {
|
|
928 |
relevanssi_update_log( $query_string, $hits_count );
|
929 |
}
|
930 |
|
931 |
-
$make_excerpts = get_option( 'relevanssi_excerpts' );
|
932 |
if ( $relevanssi_test_admin || ( $query->is_admin && ! defined( 'DOING_AJAX' ) ) ) {
|
933 |
$make_excerpts = false;
|
934 |
}
|
935 |
|
936 |
-
|
937 |
-
$search_low_boundary = ( $query->query_vars['paged'] - 1 ) * $query->query_vars['posts_per_page'];
|
938 |
-
} else {
|
939 |
-
$search_low_boundary = 0;
|
940 |
-
}
|
941 |
-
|
942 |
-
if ( ! isset( $query->query_vars['posts_per_page'] ) || -1 === $query->query_vars['posts_per_page'] ) {
|
943 |
-
$search_high_boundary = $hits_count;
|
944 |
-
} else {
|
945 |
-
$search_high_boundary = $search_low_boundary + $query->query_vars['posts_per_page'] - 1;
|
946 |
-
}
|
947 |
-
|
948 |
-
if ( isset( $query->query_vars['offset'] ) && $query->query_vars['offset'] > 0 ) {
|
949 |
-
$search_high_boundary += $query->query_vars['offset'];
|
950 |
-
$search_low_boundary += $query->query_vars['offset'];
|
951 |
-
}
|
952 |
-
|
953 |
-
if ( $search_high_boundary > $hits_count ) {
|
954 |
-
$search_high_boundary = $hits_count;
|
955 |
-
}
|
956 |
-
|
957 |
-
for ( $i = $search_low_boundary; $i <= $search_high_boundary; $i++ ) {
|
958 |
-
if ( isset( $hits[ intval( $i ) ] ) ) {
|
959 |
-
$post = $hits[ intval( $i ) ];
|
960 |
-
} else {
|
961 |
-
continue;
|
962 |
-
}
|
963 |
|
964 |
-
|
965 |
-
|
966 |
-
|
967 |
-
continue;
|
968 |
-
// @codeCoverageIgnoreEnd
|
969 |
-
}
|
970 |
|
971 |
-
|
972 |
-
|
973 |
-
|
974 |
-
|
975 |
-
|
976 |
-
|
977 |
-
|
978 |
-
|
979 |
-
|
980 |
-
|
981 |
-
|
982 |
-
|
983 |
-
|
984 |
-
|
985 |
-
|
986 |
}
|
987 |
-
|
988 |
-
|
989 |
-
* If you need to modify these on the go, use
|
990 |
-
* 'pre_option_relevanssi_excerpt_length' and
|
991 |
-
* pre_option_relevanssi_excerpt_type' filters.
|
992 |
-
*/
|
993 |
-
$excerpt_length = get_option( 'relevanssi_excerpt_length' );
|
994 |
-
$excerpt_type = get_option( 'relevanssi_excerpt_type' );
|
995 |
-
|
996 |
-
if ( 'on' === $make_excerpts && empty( $search_params['fields'] ) ) {
|
997 |
-
if ( isset( $post->blog_id ) ) {
|
998 |
-
switch_to_blog( $post->blog_id );
|
999 |
-
}
|
1000 |
-
$post->original_excerpt = $post->post_excerpt;
|
1001 |
-
$post->post_excerpt = relevanssi_do_excerpt(
|
1002 |
-
$post,
|
1003 |
-
$q,
|
1004 |
-
$excerpt_length,
|
1005 |
-
$excerpt_type
|
1006 |
-
);
|
1007 |
-
|
1008 |
-
if ( isset( $post->blog_id ) ) {
|
1009 |
-
restore_current_blog();
|
1010 |
-
}
|
1011 |
}
|
1012 |
-
if (
|
1013 |
relevanssi_add_matches( $post, $return );
|
1014 |
}
|
1015 |
-
if (
|
1016 |
$post->post_excerpt .= relevanssi_show_matches( $post );
|
1017 |
}
|
1018 |
|
@@ -1066,9 +699,8 @@ function relevanssi_limit_filter( $query ) {
|
|
1066 |
/**
|
1067 |
* Fetches the list of post types that are excluded from the search.
|
1068 |
*
|
1069 |
-
* Figures out the post types that are not included in the search.
|
1070 |
-
*
|
1071 |
-
* Tested.
|
1072 |
*
|
1073 |
* @param string $include_attachments Whether to include attachments or not.
|
1074 |
*
|
@@ -1095,6 +727,12 @@ function relevanssi_get_negative_post_type( $include_attachments ) {
|
|
1095 |
$negative_post_type_list = array_merge( $negative_post_type_list, $pt_1, $pt_2 );
|
1096 |
}
|
1097 |
|
|
|
|
|
|
|
|
|
|
|
|
|
1098 |
// Post types to exclude.
|
1099 |
if ( count( $negative_post_type_list ) > 0 ) {
|
1100 |
$negative_post_types = esc_sql( array_unique( $negative_post_type_list ) );
|
@@ -1241,6 +879,8 @@ function relevanssi_taxonomy_score( &$match, $post_type_weights ) {
|
|
1241 |
function relevanssi_compile_search_args( $query, $q ) {
|
1242 |
global $relevanssi_test_admin;
|
1243 |
|
|
|
|
|
1244 |
$tax_query = array();
|
1245 |
/**
|
1246 |
* Filters the default tax_query relation.
|
@@ -1374,51 +1014,11 @@ function relevanssi_compile_search_args( $query, $q ) {
|
|
1374 |
$parent_query = array( 'parent not in' => $query->query_vars['post_parent__not_in'] );
|
1375 |
}
|
1376 |
|
1377 |
-
$meta_query = relevanssi_meta_query_from_query_vars( $query );
|
1378 |
-
$date_query = relevanssi_wp_date_query_from_query_vars( $query );
|
1379 |
-
|
1380 |
-
$post_type = false;
|
1381 |
-
if ( isset( $query->query_vars['post_type'] ) && 'any' !== $query->query_vars['post_type'] ) {
|
1382 |
-
$post_type = $query->query_vars['post_type'];
|
1383 |
-
}
|
1384 |
-
if ( isset( $query->query_vars['post_types'] ) && 'any' !== $query->query_vars['post_types'] ) {
|
1385 |
-
$post_type = $query->query_vars['post_types'];
|
1386 |
-
}
|
1387 |
-
|
1388 |
-
$post_status = false;
|
1389 |
-
if ( isset( $query->query_vars['post_status'] ) && 'any' !== $query->query_vars['post_status'] ) {
|
1390 |
-
$post_status = $query->query_vars['post_status'];
|
1391 |
-
}
|
1392 |
-
|
1393 |
$expost = get_option( 'relevanssi_exclude_posts' );
|
1394 |
if ( $relevanssi_test_admin || ( is_admin() && ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) ) ) {
|
1395 |
$expost = null;
|
1396 |
}
|
1397 |
|
1398 |
-
$sentence = false;
|
1399 |
-
if ( isset( $query->query_vars['sentence'] ) && ! empty( $query->query_vars['sentence'] ) ) {
|
1400 |
-
$sentence = true;
|
1401 |
-
}
|
1402 |
-
|
1403 |
-
$operator = '';
|
1404 |
-
if ( function_exists( 'relevanssi_set_operator' ) ) {
|
1405 |
-
$operator = relevanssi_set_operator( $query );
|
1406 |
-
$operator = strtoupper( $operator );
|
1407 |
-
}
|
1408 |
-
if ( ! in_array( $operator, array( 'OR', 'AND' ), true ) ) {
|
1409 |
-
$operator = get_option( 'relevanssi_implicit_operator' );
|
1410 |
-
}
|
1411 |
-
$query->query_vars['operator'] = $operator;
|
1412 |
-
|
1413 |
-
$orderby = null;
|
1414 |
-
$order = null;
|
1415 |
-
if ( isset( $query->query_vars['orderby'] ) ) {
|
1416 |
-
$orderby = $query->query_vars['orderby'];
|
1417 |
-
}
|
1418 |
-
if ( isset( $query->query_vars['order'] ) ) {
|
1419 |
-
$order = $query->query_vars['order'];
|
1420 |
-
}
|
1421 |
-
|
1422 |
$fields = '';
|
1423 |
if ( ! empty( $query->query_vars['fields'] ) ) {
|
1424 |
if ( 'ids' === $query->query_vars['fields'] ) {
|
@@ -1427,26 +1027,11 @@ function relevanssi_compile_search_args( $query, $q ) {
|
|
1427 |
if ( 'id=>parent' === $query->query_vars['fields'] ) {
|
1428 |
$fields = 'id=>parent';
|
1429 |
}
|
1430 |
-
|
1431 |
-
|
1432 |
-
$by_date = '';
|
1433 |
-
if ( ! empty( $query->query_vars['by_date'] ) ) {
|
1434 |
-
if ( preg_match( '/\d+[hdmyw]/', $query->query_vars['by_date'] ) ) {
|
1435 |
-
// Accepted format is digits followed by h, d, m, y, or w.
|
1436 |
-
$by_date = $query->query_vars['by_date'];
|
1437 |
}
|
1438 |
}
|
1439 |
|
1440 |
-
$admin_search = false;
|
1441 |
-
if ( isset( $query->query_vars['relevanssi_admin_search'] ) ) {
|
1442 |
-
$admin_search = true;
|
1443 |
-
}
|
1444 |
-
|
1445 |
-
$include_attachments = '';
|
1446 |
-
if ( isset( $query->query_vars['include_attachments'] ) ) {
|
1447 |
-
$include_attachments = $query->query_vars['include_attachments'];
|
1448 |
-
}
|
1449 |
-
|
1450 |
if ( function_exists( 'relevanssi_extract_specifier' ) ) {
|
1451 |
$q = relevanssi_extract_specifier( $q );
|
1452 |
}
|
@@ -1454,33 +1039,26 @@ function relevanssi_compile_search_args( $query, $q ) {
|
|
1454 |
// Add synonyms.
|
1455 |
// This is done here so the new terms will get highlighting.
|
1456 |
$q_no_synonyms = $q;
|
1457 |
-
if ( 'OR' === $operator ) {
|
1458 |
// Synonyms are only used in OR queries.
|
1459 |
$q = relevanssi_add_synonyms( $q );
|
1460 |
}
|
1461 |
|
1462 |
-
$
|
1463 |
-
|
1464 |
-
|
1465 |
-
|
1466 |
-
|
1467 |
-
|
1468 |
-
|
1469 |
-
|
1470 |
-
|
1471 |
-
|
1472 |
-
|
1473 |
-
|
1474 |
-
|
1475 |
-
|
1476 |
-
|
1477 |
-
'order' => $order,
|
1478 |
-
'fields' => $fields,
|
1479 |
-
'sentence' => $sentence,
|
1480 |
-
'by_date' => $by_date,
|
1481 |
-
'admin_search' => $admin_search,
|
1482 |
-
'include_attachments' => $include_attachments,
|
1483 |
-
'meta_query' => $meta_query,
|
1484 |
);
|
1485 |
|
1486 |
return $search_params;
|
@@ -1616,3 +1194,656 @@ function relevanssi_meta_query_from_query_vars( $query ) {
|
|
1616 |
}
|
1617 |
return $meta_query;
|
1618 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
50 |
$search_ok = false; // No search term.
|
51 |
}
|
52 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
53 |
if ( $query->get( 'relevanssi' ) ) {
|
54 |
$search_ok = true; // Manual override, always search.
|
55 |
}
|
104 |
* @return array An array of return values.
|
105 |
*/
|
106 |
function relevanssi_search( $args ) {
|
107 |
+
global $wpdb;
|
|
|
108 |
|
109 |
/**
|
110 |
* Filters the search parameters.
|
139 |
$terms = relevanssi_tokenize( $q, $remove_stopwords, $min_length );
|
140 |
$terms = array_keys( $terms ); // Don't care about tf in query.
|
141 |
|
142 |
+
$terms_without_synonyms = relevanssi_tokenize( $q_no_synonyms, $remove_stopwords, $min_length );
|
143 |
+
$terms_without_synonyms = array_keys( $terms_without_synonyms );
|
144 |
+
|
145 |
if ( function_exists( 'relevanssi_process_terms' ) ) {
|
146 |
$process_terms_results = relevanssi_process_terms( $terms, $q );
|
147 |
$query_restrictions .= $process_terms_results['query_restrictions'];
|
184 |
}
|
185 |
}
|
186 |
|
187 |
+
$match_arrays = relevanssi_initialize_match_arrays();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
188 |
$term_hits = array();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
189 |
$include_these_posts = array();
|
190 |
$include_these_items = array();
|
|
|
191 |
$doc_weight = array();
|
192 |
+
$total_hits = 0;
|
193 |
+
$no_matches = true;
|
194 |
+
$search_again = false;
|
195 |
+
$post_type_weights = get_option( 'relevanssi_post_type_weights' );
|
196 |
+
$fuzzy = get_option( 'relevanssi_fuzzy' );
|
197 |
|
198 |
do {
|
199 |
+
$df_counts = relevanssi_generate_df_counts(
|
200 |
+
$terms,
|
201 |
+
array(
|
202 |
+
'no_terms' => $no_terms,
|
203 |
+
'operator' => $operator,
|
204 |
+
'phrase_queries' => $phrase_queries,
|
205 |
+
'query_join' => $query_join,
|
206 |
+
'query_restrictions' => $query_restrictions,
|
207 |
+
'search_again' => $search_again,
|
208 |
+
)
|
209 |
+
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
210 |
|
211 |
foreach ( $df_counts as $term => $df ) {
|
|
|
|
|
212 |
$this_query_restrictions = relevanssi_add_phrase_restrictions(
|
213 |
$query_restrictions,
|
214 |
$phrase_queries,
|
216 |
$operator
|
217 |
);
|
218 |
|
219 |
+
$query = relevanssi_generate_search_query( $term, $search_again, $no_terms, $query_join, $this_query_restrictions );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
220 |
$matches = $wpdb->get_results( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
221 |
+
|
222 |
if ( count( $matches ) < 1 ) {
|
223 |
continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
224 |
}
|
225 |
|
226 |
+
$no_matches = false;
|
227 |
+
relevanssi_add_include_matches(
|
228 |
+
$matches,
|
229 |
+
array(
|
230 |
+
'posts' => $include_these_posts,
|
231 |
+
'items' => $include_these_items,
|
232 |
+
),
|
233 |
+
array(
|
234 |
+
'term' => $term,
|
235 |
+
'search_again' => $search_again,
|
236 |
+
'no_terms' => $no_terms,
|
237 |
+
)
|
238 |
+
);
|
239 |
+
|
240 |
relevanssi_populate_array( $matches );
|
241 |
|
242 |
$total_hits += count( $matches );
|
248 |
}
|
249 |
|
250 |
foreach ( $matches as $match ) {
|
251 |
+
$match->doc = relevanssi_adjust_match_doc( $match );
|
252 |
+
$match->tf = relevanssi_calculate_tf( $match, $post_type_weights );
|
253 |
+
$match->weight = relevanssi_calculate_weight( $match, $idf, $post_type_weights, $q );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
254 |
|
255 |
/**
|
256 |
+
* Filters the Relevanssi post matches.
|
257 |
+
*
|
258 |
+
* This powerful filter lets you modify the $match objects,
|
259 |
+
* which are used to calculate the weight of the documents. The
|
260 |
+
* object has attributes which contain the number of hits in
|
261 |
+
* different categories.
|
262 |
*
|
263 |
+
* Post ID is $match->doc, term frequency (TF) is
|
264 |
+
* $match->tf and the total weight is in $match->weight. The
|
265 |
+
* filter is also passed $idf, which is the inverse document
|
266 |
+
* frequency (IDF). The weight is calculated as TF * IDF, which
|
267 |
+
* means you may need the IDF, if you wish to recalculate the
|
268 |
+
* weight for some reason. The third parameter, $term, contains
|
269 |
+
* the search term.
|
270 |
*
|
271 |
+
* @param object $match The match object, with includes all
|
272 |
+
* the different categories of post matches.
|
273 |
+
* @param int $idf The inverse document frequency, in
|
274 |
+
* case you want to recalculate TF * IDF weights.
|
275 |
+
* @param string $term The search term.
|
276 |
*/
|
277 |
$match = apply_filters( 'relevanssi_match', $match, $idf, $term );
|
278 |
if ( $match->weight <= 0 ) {
|
293 |
*/
|
294 |
$post_ok = apply_filters( 'relevanssi_post_ok', $post_ok, $match->doc );
|
295 |
if ( $post_ok ) {
|
296 |
+
relevanssi_update_term_hits( $term_hits, $match_arrays, $match, $term );
|
297 |
+
|
298 |
$doc_terms[ $match->doc ][ $term ] = true; // Count how many terms are matched to a doc.
|
299 |
if ( ! isset( $doc_weight[ $match->doc ] ) ) {
|
300 |
$doc_weight[ $match->doc ] = 0;
|
333 |
/**
|
334 |
* Filters the parameters for fallback search.
|
335 |
*
|
336 |
+
* If you want to make Relevanssi search again with different
|
337 |
+
* parameters, you can use this filter hook to adjust the parameters.
|
338 |
+
* Set $params['search_again'] to true to make Relevanssi do a new search.
|
339 |
*
|
340 |
* @param array The search parameters.
|
341 |
*/
|
356 |
}
|
357 |
$total_terms = count( $terms_without_stops );
|
358 |
|
359 |
+
$temp_terms_without_stops_synonyms = array_keys( relevanssi_tokenize( implode( ' ', $terms_without_synonyms ), $strip_stops ) );
|
360 |
+
$terms_without_stops_synonyms = array();
|
361 |
+
foreach ( $temp_terms_without_stops_synonyms as $temp_term ) {
|
362 |
+
if ( relevanssi_strlen( $temp_term ) >= $min_length ) {
|
363 |
+
array_push( $terms_without_stops_synonyms, $temp_term );
|
364 |
+
}
|
365 |
+
}
|
366 |
+
|
367 |
if ( isset( $doc_weight ) ) {
|
368 |
/**
|
369 |
* Filters the results Relevanssi finds.
|
376 |
$doc_weight = apply_filters( 'relevanssi_results', $doc_weight );
|
377 |
}
|
378 |
|
379 |
+
$missing_terms = array();
|
380 |
+
|
381 |
if ( isset( $doc_weight ) && count( $doc_weight ) > 0 ) {
|
382 |
arsort( $doc_weight );
|
383 |
$i = 0;
|
387 |
// doc didn't match all terms, so it's discarded.
|
388 |
continue;
|
389 |
}
|
390 |
+
if ( count( $doc_terms[ $doc ] ) < $total_terms ) {
|
391 |
+
$missing_terms[ $doc ] = array_diff(
|
392 |
+
array_values( $terms_without_stops_synonyms ),
|
393 |
+
array_keys( $doc_terms[ $doc ] )
|
394 |
+
);
|
395 |
+
if ( count( $missing_terms[ $doc ] ) === count( $terms_without_stops_synonyms ) ) {
|
396 |
+
$missing_terms[ $doc ] = array_diff(
|
397 |
+
array_values( $terms_without_stops_synonyms ),
|
398 |
+
relevanssi_replace_synonyms_in_terms( array_keys( $doc_terms[ $doc ] ) )
|
399 |
+
);
|
400 |
+
}
|
401 |
+
}
|
402 |
|
403 |
if ( ! empty( $fields ) ) {
|
404 |
if ( 'ids' === $fields ) {
|
405 |
$hits[ intval( $i ) ] = $doc;
|
406 |
}
|
407 |
if ( 'id=>parent' === $fields ) {
|
408 |
+
$hits[ intval( $i ) ] = relevanssi_generate_post_parent( $doc );
|
409 |
+
}
|
410 |
+
if ( 'id=>type' === $fields ) {
|
411 |
+
$hits[ intval( $i ) ] = relevanssi_generate_id_type( $doc );
|
|
|
412 |
}
|
413 |
} else {
|
414 |
$hits[ intval( $i ) ] = relevanssi_get_post( $doc );
|
415 |
$hits[ intval( $i ) ]->relevance_score = round( $weight, 2 );
|
416 |
+
|
417 |
+
if ( isset( $missing_terms[ $doc ] ) ) {
|
418 |
+
$hits[ intval( $i ) ]->missing_terms = $missing_terms[ $doc ];
|
419 |
+
}
|
420 |
}
|
421 |
$i++;
|
422 |
}
|
433 |
$or_args['q'] = relevanssi_add_synonyms( $q );
|
434 |
$return = relevanssi_search( $or_args );
|
435 |
|
436 |
+
$hits = $return['hits'];
|
437 |
+
$match_arrays['body'] = $return['body_matches'];
|
438 |
+
$match_arrays['title'] = $return['title_matches'];
|
439 |
+
$match_arrays['tag'] = $return['tag_matches'];
|
440 |
+
$match_arrays['category'] = $return['category_matches'];
|
441 |
+
$match_arrays['taxonomy'] = $return['taxonomy_matches'];
|
442 |
+
$match_arrays['comment'] = $return['comment_matches'];
|
443 |
+
$match_arrays['link'] = $return['link_matches'];
|
444 |
+
$match_arrays['author'] = $return['author_matches'];
|
445 |
+
$match_arrays['customfield'] = $return['customfield_matches'];
|
446 |
+
$match_arrays['mysqlcolumn'] = $return['mysqlcolumn_matches'];
|
447 |
+
$match_arrays['excerpt'] = $return['excerpt_matches'];
|
448 |
+
$term_hits = $return['term_hits'];
|
449 |
+
$doc_weight = $return['doc_weights'];
|
450 |
+
$q = $return['query'];
|
451 |
}
|
452 |
$params = array( 'args' => $args );
|
453 |
/**
|
462 |
$params = apply_filters( 'relevanssi_fallback', $params );
|
463 |
$args = $params['args'];
|
464 |
if ( isset( $params['return'] ) ) {
|
465 |
+
$return = $params['return'];
|
466 |
+
$hits = $return['hits'];
|
467 |
+
$match_arrays['body'] = $return['body_matches'];
|
468 |
+
$match_arrays['title'] = $return['title_matches'];
|
469 |
+
$match_arrays['tag'] = $return['tag_matches'];
|
470 |
+
$match_arrays['category'] = $return['category_matches'];
|
471 |
+
$match_arrays['taxonomy'] = $return['taxonomy_matches'];
|
472 |
+
$match_arrays['comment'] = $return['comment_matches'];
|
473 |
+
$match_arrays['link'] = $return['link_matches'];
|
474 |
+
$match_arrays['author'] = $return['author_matches'];
|
475 |
+
$match_arrays['customfield'] = $return['customfield_matches'];
|
476 |
+
$match_arrays['mysqlcolumn'] = $return['mysqlcolumn_matches'];
|
477 |
+
$match_arrays['excerpt'] = $return['excerpt_matches'];
|
478 |
+
$term_hits = $return['term_hits'];
|
479 |
+
$doc_weight = $return['doc_weights'];
|
480 |
+
$q = $return['query'];
|
481 |
}
|
482 |
}
|
483 |
|
484 |
+
relevanssi_sort_results( $hits, $orderby, $order, $meta_query );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
485 |
|
|
|
|
|
|
|
|
|
|
|
486 |
$return = array(
|
487 |
'hits' => $hits,
|
488 |
+
'body_matches' => $match_arrays['body'],
|
489 |
+
'title_matches' => $match_arrays['title'],
|
490 |
+
'tag_matches' => $match_arrays['tag'],
|
491 |
+
'category_matches' => $match_arrays['category'],
|
492 |
+
'comment_matches' => $match_arrays['comment'],
|
493 |
+
'taxonomy_matches' => $match_arrays['taxonomy'],
|
494 |
+
'link_matches' => $match_arrays['link'],
|
495 |
+
'customfield_matches' => $match_arrays['customfield'],
|
496 |
+
'mysqlcolumn_matches' => $match_arrays['mysqlcolumn'],
|
497 |
+
'author_matches' => $match_arrays['author'],
|
498 |
+
'excerpt_matches' => $match_arrays['excerpt'],
|
499 |
'term_hits' => $term_hits,
|
500 |
'query' => $q,
|
501 |
'doc_weights' => $doc_weight,
|
502 |
'query_no_synonyms' => $q_no_synonyms,
|
503 |
+
'missing_terms' => $missing_terms,
|
504 |
);
|
505 |
|
506 |
return $return;
|
612 |
relevanssi_update_log( $query_string, $hits_count );
|
613 |
}
|
614 |
|
615 |
+
$make_excerpts = 'on' === get_option( 'relevanssi_excerpts' ) ? true : false;
|
616 |
if ( $relevanssi_test_admin || ( $query->is_admin && ! defined( 'DOING_AJAX' ) ) ) {
|
617 |
$make_excerpts = false;
|
618 |
}
|
619 |
|
620 |
+
list( $search_low_boundary, $search_high_boundary ) = relevanssi_get_boundaries( $query );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
621 |
|
622 |
+
$highlight_title = 'on' === get_option( 'relevanssi_hilite_title' ) ? true : false;
|
623 |
+
$show_matches = 'on' === get_option( 'relevanssi_show_matches' ) ? true : false;
|
624 |
+
$return_posts = empty( $search_params['fields'] );
|
|
|
|
|
|
|
625 |
|
626 |
+
$hits_to_show = array_slice( $hits, $search_low_boundary, $search_high_boundary - $search_low_boundary + 1 );
|
627 |
+
/**
|
628 |
+
* Filters the displayed hits.
|
629 |
+
*
|
630 |
+
* Similar to 'relevanssi_hits_filter', but only filters the posts that
|
631 |
+
* are displayed on the search results page. Don't make big changes here.
|
632 |
+
*
|
633 |
+
* @param array $hits_to_show An array of post objects.
|
634 |
+
* @param WP_Query $query The WP Query object.
|
635 |
+
*
|
636 |
+
* @return array An array of post objects.
|
637 |
+
*/
|
638 |
+
foreach ( apply_filters( 'relevanssi_hits_to_show', $hits_to_show, $query ) as $post ) {
|
639 |
+
if ( $highlight_title && $return_posts ) {
|
640 |
+
relevanssi_highlight_post_title( $post, $q );
|
641 |
}
|
642 |
+
if ( $make_excerpts && $return_posts ) {
|
643 |
+
relevanssi_add_excerpt( $post, $q );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
644 |
}
|
645 |
+
if ( $return_posts ) {
|
646 |
relevanssi_add_matches( $post, $return );
|
647 |
}
|
648 |
+
if ( $show_matches && $return_posts ) {
|
649 |
$post->post_excerpt .= relevanssi_show_matches( $post );
|
650 |
}
|
651 |
|
699 |
/**
|
700 |
* Fetches the list of post types that are excluded from the search.
|
701 |
*
|
702 |
+
* Figures out the post types that are not included in the search. Only includes
|
703 |
+
* the post types that are actually indexed.
|
|
|
704 |
*
|
705 |
* @param string $include_attachments Whether to include attachments or not.
|
706 |
*
|
727 |
$negative_post_type_list = array_merge( $negative_post_type_list, $pt_1, $pt_2 );
|
728 |
}
|
729 |
|
730 |
+
$indexed_post_types = get_option( 'relevanssi_index_post_types', array() );
|
731 |
+
$negative_post_type_list = array_intersect(
|
732 |
+
$negative_post_type_list,
|
733 |
+
$indexed_post_types
|
734 |
+
);
|
735 |
+
|
736 |
// Post types to exclude.
|
737 |
if ( count( $negative_post_type_list ) > 0 ) {
|
738 |
$negative_post_types = esc_sql( array_unique( $negative_post_type_list ) );
|
879 |
function relevanssi_compile_search_args( $query, $q ) {
|
880 |
global $relevanssi_test_admin;
|
881 |
|
882 |
+
$search_params = relevanssi_compile_common_args( $query );
|
883 |
+
|
884 |
$tax_query = array();
|
885 |
/**
|
886 |
* Filters the default tax_query relation.
|
1014 |
$parent_query = array( 'parent not in' => $query->query_vars['post_parent__not_in'] );
|
1015 |
}
|
1016 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1017 |
$expost = get_option( 'relevanssi_exclude_posts' );
|
1018 |
if ( $relevanssi_test_admin || ( is_admin() && ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) ) ) {
|
1019 |
$expost = null;
|
1020 |
}
|
1021 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1022 |
$fields = '';
|
1023 |
if ( ! empty( $query->query_vars['fields'] ) ) {
|
1024 |
if ( 'ids' === $query->query_vars['fields'] ) {
|
1027 |
if ( 'id=>parent' === $query->query_vars['fields'] ) {
|
1028 |
$fields = 'id=>parent';
|
1029 |
}
|
1030 |
+
if ( 'id=>type' === $query->query_vars['fields'] ) {
|
1031 |
+
$fields = 'id=>type';
|
|
|
|
|
|
|
|
|
|
|
1032 |
}
|
1033 |
}
|
1034 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1035 |
if ( function_exists( 'relevanssi_extract_specifier' ) ) {
|
1036 |
$q = relevanssi_extract_specifier( $q );
|
1037 |
}
|
1039 |
// Add synonyms.
|
1040 |
// This is done here so the new terms will get highlighting.
|
1041 |
$q_no_synonyms = $q;
|
1042 |
+
if ( 'OR' === $search_params['operator'] ) {
|
1043 |
// Synonyms are only used in OR queries.
|
1044 |
$q = relevanssi_add_synonyms( $q );
|
1045 |
}
|
1046 |
|
1047 |
+
$query->query_vars['operator'] = $search_params['operator'];
|
1048 |
+
|
1049 |
+
$search_params = array_merge(
|
1050 |
+
$search_params,
|
1051 |
+
array(
|
1052 |
+
'q' => $q,
|
1053 |
+
'q_no_synonyms' => $q_no_synonyms,
|
1054 |
+
'tax_query' => $tax_query,
|
1055 |
+
'tax_query_relation' => $tax_query_relation,
|
1056 |
+
'post_query' => $post_query,
|
1057 |
+
'parent_query' => $parent_query,
|
1058 |
+
'expost' => $expost,
|
1059 |
+
'author' => $author,
|
1060 |
+
'fields' => $fields,
|
1061 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1062 |
);
|
1063 |
|
1064 |
return $search_params;
|
1194 |
}
|
1195 |
return $meta_query;
|
1196 |
}
|
1197 |
+
|
1198 |
+
/**
|
1199 |
+
* Checks whether Relevanssi can do a media search.
|
1200 |
+
*
|
1201 |
+
* Relevanssi does not work with the grid view of Media Gallery. This function
|
1202 |
+
* will disable Relevanssi a) if Relevanssi is not set to index attachments,
|
1203 |
+
* b) if Relevanssi is not set to index image attachments and c) if the Media
|
1204 |
+
* Library is in grid mode. Any of these will inactivate Relevanssi in the
|
1205 |
+
* Media Library search.
|
1206 |
+
*
|
1207 |
+
* @param boolean $search_ok If true, allow the search.
|
1208 |
+
* @param WP_Query $query The query object.
|
1209 |
+
*
|
1210 |
+
* @return boolean If true, allow the search.
|
1211 |
+
*/
|
1212 |
+
function relevanssi_control_media_queries( bool $search_ok, WP_Query $query ) : bool {
|
1213 |
+
if ( ! $search_ok ) {
|
1214 |
+
// Something else has already disabled the search, this won't enable.
|
1215 |
+
return $search_ok;
|
1216 |
+
}
|
1217 |
+
if ( ! isset( $query->query_vars['post_type'] ) || ! isset( $query->query_vars['post_status'] ) ) {
|
1218 |
+
// Not a Media Library search.
|
1219 |
+
return $search_ok;
|
1220 |
+
}
|
1221 |
+
if (
|
1222 |
+
'attachment' !== $query->query_vars['post_type'] &&
|
1223 |
+
'inherit,private' !== $query->query_vars['post_status']
|
1224 |
+
) {
|
1225 |
+
// Not a Media Library search.
|
1226 |
+
return $search_ok;
|
1227 |
+
}
|
1228 |
+
$indexed_post_types = array_flip(
|
1229 |
+
get_option( 'relevanssi_index_post_types', array() )
|
1230 |
+
);
|
1231 |
+
$images_indexed = get_option( 'relevanssi_index_image_files', 'off' );
|
1232 |
+
if ( false === isset( $indexed_post_types['attachment'] ) || 'off' === $images_indexed ) {
|
1233 |
+
// Attachments or images are not indexed, disable.
|
1234 |
+
$search_ok = false;
|
1235 |
+
}
|
1236 |
+
|
1237 |
+
if ( ! isset( $_REQUEST['mode'] ) || 'list' !== $_REQUEST['mode'] ) { // phpcs:ignore WordPress.Security.NonceVerification
|
1238 |
+
// Grid view, disable.
|
1239 |
+
$search_ok = false;
|
1240 |
+
}
|
1241 |
+
|
1242 |
+
return $search_ok;
|
1243 |
+
}
|
1244 |
+
|
1245 |
+
/**
|
1246 |
+
* Calculates the TF value.
|
1247 |
+
*
|
1248 |
+
* @param stdClass $match The match object.
|
1249 |
+
* @param array $post_type_weights An array of post type weights.
|
1250 |
+
*
|
1251 |
+
* @return float The TF value.
|
1252 |
+
*/
|
1253 |
+
function relevanssi_calculate_tf( $match, $post_type_weights ) {
|
1254 |
+
$content_boost = floatval( get_option( 'relevanssi_content_boost', 1 ) );
|
1255 |
+
$title_boost = floatval( get_option( 'relevanssi_title_boost' ) );
|
1256 |
+
$link_boost = floatval( get_option( 'relevanssi_link_boost' ) );
|
1257 |
+
$comment_boost = floatval( get_option( 'relevanssi_comment_boost' ) );
|
1258 |
+
|
1259 |
+
if ( ! empty( $match->taxonomy_detail ) ) {
|
1260 |
+
relevanssi_taxonomy_score( $match, $post_type_weights );
|
1261 |
+
} else {
|
1262 |
+
$tag_weight = 1;
|
1263 |
+
if ( isset( $post_type_weights['post_tagged_with_post_tag'] ) && is_numeric( $post_type_weights['post_tagged_with_post_tag'] ) ) {
|
1264 |
+
$tag_weight = $post_type_weights['post_tagged_with_post_tag'];
|
1265 |
+
}
|
1266 |
+
|
1267 |
+
$category_weight = 1;
|
1268 |
+
if ( isset( $post_type_weights['post_tagged_with_category'] ) && is_numeric( $post_type_weights['post_tagged_with_category'] ) ) {
|
1269 |
+
$category_weight = $post_type_weights['post_tagged_with_category'];
|
1270 |
+
}
|
1271 |
+
|
1272 |
+
$taxonomy_weight = 1;
|
1273 |
+
|
1274 |
+
$match->taxonomy_score =
|
1275 |
+
$match->tag * $tag_weight +
|
1276 |
+
$match->category * $category_weight +
|
1277 |
+
$match->taxonomy * $taxonomy_weight;
|
1278 |
+
}
|
1279 |
+
|
1280 |
+
$tf =
|
1281 |
+
$match->title * $title_boost +
|
1282 |
+
$match->content * $content_boost +
|
1283 |
+
$match->comment * $comment_boost +
|
1284 |
+
$match->link * $link_boost +
|
1285 |
+
$match->author +
|
1286 |
+
$match->excerpt +
|
1287 |
+
$match->taxonomy_score +
|
1288 |
+
$match->customfield +
|
1289 |
+
$match->mysqlcolumn;
|
1290 |
+
|
1291 |
+
return $tf;
|
1292 |
+
}
|
1293 |
+
|
1294 |
+
/**
|
1295 |
+
* Calculates the match weight based on TF, IDF and bonus multipliers.
|
1296 |
+
*
|
1297 |
+
* @param stdClass $match The match object.
|
1298 |
+
* @param float $idf The inverse document frequency.
|
1299 |
+
* @param array $post_type_weights The post type weights.
|
1300 |
+
* @param string $query The search query.
|
1301 |
+
*
|
1302 |
+
* @return float The weight.
|
1303 |
+
*/
|
1304 |
+
function relevanssi_calculate_weight( $match, $idf, $post_type_weights, $query ) {
|
1305 |
+
if ( $idf < 1 ) {
|
1306 |
+
$idf = 1;
|
1307 |
+
}
|
1308 |
+
$weight = $match->tf * $idf;
|
1309 |
+
|
1310 |
+
$type = relevanssi_get_post_type( $match->doc );
|
1311 |
+
if ( ! is_wp_error( $type ) && ! empty( $post_type_weights[ $type ] ) ) {
|
1312 |
+
$weight = $weight * $post_type_weights[ $type ];
|
1313 |
+
}
|
1314 |
+
|
1315 |
+
/* Weight boost for taxonomy terms based on taxonomy. */
|
1316 |
+
if ( ! empty( $post_type_weights[ 'taxonomy_term_' . $match->type ] ) ) {
|
1317 |
+
$weight = $weight * $post_type_weights[ 'taxonomy_term_' . $match->type ];
|
1318 |
+
}
|
1319 |
+
|
1320 |
+
if ( function_exists( 'relevanssi_get_recency_bonus' ) ) {
|
1321 |
+
$recency_details = relevanssi_get_recency_bonus();
|
1322 |
+
$recency_bonus = $recency_details['bonus'];
|
1323 |
+
$recency_cutoff_date = $recency_details['cutoff'];
|
1324 |
+
if ( $recency_bonus ) {
|
1325 |
+
$post = relevanssi_get_post( $match->doc );
|
1326 |
+
if ( strtotime( $post->post_date ) > $recency_cutoff_date ) {
|
1327 |
+
$weight = $weight * $recency_bonus;
|
1328 |
+
}
|
1329 |
+
}
|
1330 |
+
}
|
1331 |
+
|
1332 |
+
if ( $query && 'on' === get_option( 'relevanssi_exact_match_bonus' ) ) {
|
1333 |
+
/**
|
1334 |
+
* Filters the exact match bonus.
|
1335 |
+
*
|
1336 |
+
* @param array The title bonus under 'title' (default 5) and the content
|
1337 |
+
* bonus under 'content' (default 2).
|
1338 |
+
*/
|
1339 |
+
$exact_match_boost = apply_filters(
|
1340 |
+
'relevanssi_exact_match_bonus',
|
1341 |
+
array(
|
1342 |
+
'title' => 5,
|
1343 |
+
'content' => 2,
|
1344 |
+
)
|
1345 |
+
);
|
1346 |
+
|
1347 |
+
$post = relevanssi_get_post( $match->doc );
|
1348 |
+
$clean_query = str_replace( '"', '', $query );
|
1349 |
+
if ( stristr( $post->post_title, $clean_query ) !== false ) {
|
1350 |
+
$weight *= $exact_match_boost['title'];
|
1351 |
+
}
|
1352 |
+
if ( stristr( $post->post_content, $clean_query ) !== false ) {
|
1353 |
+
$weight *= $exact_match_boost['content'];
|
1354 |
+
}
|
1355 |
+
}
|
1356 |
+
|
1357 |
+
return $weight;
|
1358 |
+
}
|
1359 |
+
|
1360 |
+
/**
|
1361 |
+
* Updates the $term_hits array used for showing how many hits were found for
|
1362 |
+
* each term.
|
1363 |
+
*
|
1364 |
+
* @param array $term_hits The term hits array (passed as reference).
|
1365 |
+
* @param array $match_arrays The matches array (passed as reference).
|
1366 |
+
* @param stdClass $match The match object.
|
1367 |
+
* @param string $term The search term.
|
1368 |
+
*/
|
1369 |
+
function relevanssi_update_term_hits( &$term_hits, &$match_arrays, $match, $term ) {
|
1370 |
+
$term_hits[ $match->doc ][ $term ] =
|
1371 |
+
$match->title +
|
1372 |
+
$match->content +
|
1373 |
+
$match->comment +
|
1374 |
+
$match->tag +
|
1375 |
+
$match->link +
|
1376 |
+
$match->author +
|
1377 |
+
$match->category +
|
1378 |
+
$match->excerpt +
|
1379 |
+
$match->taxonomy +
|
1380 |
+
$match->customfield;
|
1381 |
+
|
1382 |
+
relevanssi_increase_value( $match_arrays['body'][ $match->doc ], $match->content );
|
1383 |
+
relevanssi_increase_value( $match_arrays['title'][ $match->doc ], $match->title );
|
1384 |
+
relevanssi_increase_value( $match_arrays['link'][ $match->doc ], $match->link );
|
1385 |
+
relevanssi_increase_value( $match_arrays['tag'][ $match->doc ], $match->tag );
|
1386 |
+
relevanssi_increase_value( $match_arrays['category'][ $match->doc ], $match->category );
|
1387 |
+
relevanssi_increase_value( $match_arrays['taxonomy'][ $match->doc ], $match->taxonomy );
|
1388 |
+
relevanssi_increase_value( $match_arrays['comment'][ $match->doc ], $match->comment );
|
1389 |
+
relevanssi_increase_value( $match_arrays['customfield'][ $match->doc ], $match->customfield );
|
1390 |
+
relevanssi_increase_value( $match_arrays['author'][ $match->doc ], $match->author );
|
1391 |
+
relevanssi_increase_value( $match_arrays['excerpt'][ $match->doc ], $match->excerpt );
|
1392 |
+
relevanssi_increase_value( $match_arrays['mysqlcolumn'][ $match->doc ], $match->mysqlcolumn );
|
1393 |
+
}
|
1394 |
+
|
1395 |
+
/**
|
1396 |
+
* Increases a value. If it's not set, sets it first to the default value.
|
1397 |
+
*
|
1398 |
+
* @param int $value The value to increase (passed by reference).
|
1399 |
+
* @param int $increase The amount to increase the value.
|
1400 |
+
* @param int $default The default value, default 0.
|
1401 |
+
*/
|
1402 |
+
function relevanssi_increase_value( &$value, $increase, $default = 0 ) {
|
1403 |
+
if ( ! isset( $value ) ) {
|
1404 |
+
$value = $default;
|
1405 |
+
}
|
1406 |
+
$value += $increase;
|
1407 |
+
}
|
1408 |
+
|
1409 |
+
/**
|
1410 |
+
* Initializes the matches array with empty arrays.
|
1411 |
+
*
|
1412 |
+
* @return array An array of empty arrays.
|
1413 |
+
*/
|
1414 |
+
function relevanssi_initialize_match_arrays() {
|
1415 |
+
return array(
|
1416 |
+
'author' => array(),
|
1417 |
+
'body' => array(),
|
1418 |
+
'category' => array(),
|
1419 |
+
'comment' => array(),
|
1420 |
+
'customfield' => array(),
|
1421 |
+
'excerpt' => array(),
|
1422 |
+
'link' => array(),
|
1423 |
+
'mysqlcolumn' => array(),
|
1424 |
+
'tag' => array(),
|
1425 |
+
'taxonomy' => array(),
|
1426 |
+
'title' => array(),
|
1427 |
+
);
|
1428 |
+
}
|
1429 |
+
|
1430 |
+
/**
|
1431 |
+
* Calculates the DF counts for each term.
|
1432 |
+
*
|
1433 |
+
* @param array $terms The list of terms.
|
1434 |
+
* @param array $args The rest of the parameters: bool 'no_terms' for whether
|
1435 |
+
* there's a search term or not; string 'operator' for the search operator,
|
1436 |
+
* array 'phrase_queries' for the phrase queries, string 'query_join' for the
|
1437 |
+
* MySQL query JOIN value, string 'query_restrictions' for the MySQL query
|
1438 |
+
* restrictions, bool 'search_again' to tell if this is a redone search.
|
1439 |
+
*
|
1440 |
+
* @return array An array of DF values for each term.
|
1441 |
+
*/
|
1442 |
+
function relevanssi_generate_df_counts( array $terms, array $args ) : array {
|
1443 |
+
global $wpdb, $relevanssi_variables;
|
1444 |
+
$relevanssi_table = $relevanssi_variables['relevanssi_table'];
|
1445 |
+
|
1446 |
+
$fuzzy = get_option( 'relevanssi_fuzzy' );
|
1447 |
+
|
1448 |
+
$df_counts = array();
|
1449 |
+
foreach ( $terms as $term ) {
|
1450 |
+
$term_cond = relevanssi_generate_term_where( $term, $args['search_again'], $args['no_terms'] );
|
1451 |
+
if ( null === $term_cond ) {
|
1452 |
+
continue;
|
1453 |
+
}
|
1454 |
+
|
1455 |
+
$this_query_restrictions = relevanssi_add_phrase_restrictions(
|
1456 |
+
$args['query_restrictions'],
|
1457 |
+
$args['phrase_queries'],
|
1458 |
+
$term,
|
1459 |
+
$args['operator']
|
1460 |
+
);
|
1461 |
+
|
1462 |
+
$query = "SELECT COUNT(DISTINCT(relevanssi.doc)) FROM $relevanssi_table AS relevanssi
|
1463 |
+
{$args['query_join']} WHERE $term_cond $this_query_restrictions";
|
1464 |
+
// Clean: $this_query_restrictions is escaped, $term_cond is escaped.
|
1465 |
+
/**
|
1466 |
+
* Filters the DF query.
|
1467 |
+
*
|
1468 |
+
* This query is used to calculate the df for the tf * idf calculations.
|
1469 |
+
*
|
1470 |
+
* @param string MySQL query to filter.
|
1471 |
+
*/
|
1472 |
+
$query = apply_filters( 'relevanssi_df_query_filter', $query );
|
1473 |
+
|
1474 |
+
$df = $wpdb->get_var( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
1475 |
+
|
1476 |
+
if ( $df < 1 && 'sometimes' === $fuzzy ) {
|
1477 |
+
$term_cond = relevanssi_generate_term_where( $term, true, $args['no_terms'] );
|
1478 |
+
$query = "
|
1479 |
+
SELECT COUNT(DISTINCT(relevanssi.doc))
|
1480 |
+
FROM $relevanssi_table AS relevanssi
|
1481 |
+
{$args['query_join']} WHERE $term_cond {$args['query_restrictions']}";
|
1482 |
+
// Clean: $query_restrictions is escaped, $term is escaped.
|
1483 |
+
/** Documented in lib/search.php. */
|
1484 |
+
$query = apply_filters( 'relevanssi_df_query_filter', $query );
|
1485 |
+
$df = $wpdb->get_var( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
1486 |
+
}
|
1487 |
+
|
1488 |
+
$df_counts[ $term ] = $df;
|
1489 |
+
}
|
1490 |
+
|
1491 |
+
// Sort the terms in ascending DF order, so that rarest terms are searched
|
1492 |
+
// for first. This is to make sure the throttle doesn't cut off posts with
|
1493 |
+
// rare search terms.
|
1494 |
+
asort( $df_counts );
|
1495 |
+
|
1496 |
+
return $df_counts;
|
1497 |
+
}
|
1498 |
+
|
1499 |
+
/**
|
1500 |
+
* Sorts the results Relevanssi finds.
|
1501 |
+
*
|
1502 |
+
* @param array $hits The results array (passed as reference).
|
1503 |
+
* @param string|array $orderby The orderby parameter, accepts both string
|
1504 |
+
* and array format.
|
1505 |
+
* @param string $order Either 'asc' or 'desc'.
|
1506 |
+
* @param array $meta_query The meta query parameters.
|
1507 |
+
*/
|
1508 |
+
function relevanssi_sort_results( &$hits, $orderby, $order, $meta_query ) {
|
1509 |
+
if ( empty( $orderby ) ) {
|
1510 |
+
$orderby = get_option( 'relevanssi_default_orderby', 'relevance' );
|
1511 |
+
}
|
1512 |
+
|
1513 |
+
if ( is_array( $orderby ) ) {
|
1514 |
+
/**
|
1515 |
+
* Filters the orderby parameter before Relevanssi sorts posts.
|
1516 |
+
*
|
1517 |
+
* @param array|string $orderby The orderby parameter, accepts both
|
1518 |
+
* string and array format.
|
1519 |
+
*/
|
1520 |
+
$orderby = apply_filters( 'relevanssi_orderby', $orderby );
|
1521 |
+
relevanssi_object_sort( $hits, $orderby, $meta_query );
|
1522 |
+
} else {
|
1523 |
+
if ( empty( $order ) ) {
|
1524 |
+
$order = 'desc';
|
1525 |
+
}
|
1526 |
+
|
1527 |
+
$order = strtolower( $order );
|
1528 |
+
$order_accepted_values = array( 'asc', 'desc' );
|
1529 |
+
if ( ! in_array( $order, $order_accepted_values, true ) ) {
|
1530 |
+
$order = 'desc';
|
1531 |
+
}
|
1532 |
+
/**
|
1533 |
+
* This filter is documented in lib/search.php.
|
1534 |
+
*/
|
1535 |
+
$orderby = apply_filters( 'relevanssi_orderby', $orderby );
|
1536 |
+
|
1537 |
+
/**
|
1538 |
+
* Filters the order parameter before Relevanssi sorts posts.
|
1539 |
+
*
|
1540 |
+
* @param string $order The order parameter, either 'asc' or 'desc'.
|
1541 |
+
* Default 'desc'.
|
1542 |
+
*/
|
1543 |
+
$order = apply_filters( 'relevanssi_order', $order );
|
1544 |
+
|
1545 |
+
if ( 'relevance' !== $orderby ) {
|
1546 |
+
// Results are by default sorted by relevance, so no need to sort
|
1547 |
+
// for that.
|
1548 |
+
$orderby_array = array( $orderby => $order );
|
1549 |
+
relevanssi_object_sort( $hits, $orderby_array, $meta_query );
|
1550 |
+
}
|
1551 |
+
}
|
1552 |
+
}
|
1553 |
+
|
1554 |
+
/**
|
1555 |
+
* Adjusts the $match->doc ID in case of users, post type archives and
|
1556 |
+
* taxonomy terms.
|
1557 |
+
*
|
1558 |
+
* @param stdClass $match The match object.
|
1559 |
+
*
|
1560 |
+
* @return int|string The doc ID, modified if necessary.
|
1561 |
+
*/
|
1562 |
+
function relevanssi_adjust_match_doc( $match ) {
|
1563 |
+
$doc = $match->doc;
|
1564 |
+
if ( 'user' === $match->type ) {
|
1565 |
+
$doc = 'u_' . $match->item;
|
1566 |
+
} elseif ( 'post_type' === $match->type ) {
|
1567 |
+
$doc = 'p_' . $match->item;
|
1568 |
+
} elseif ( ! in_array( $match->type, array( 'post', 'attachment' ), true ) ) {
|
1569 |
+
$doc = '**' . $match->type . '**' . $match->item;
|
1570 |
+
}
|
1571 |
+
return $doc;
|
1572 |
+
}
|
1573 |
+
|
1574 |
+
/**
|
1575 |
+
* Generates the MySQL search query.
|
1576 |
+
*
|
1577 |
+
* @param string $term The search term.
|
1578 |
+
* @param bool $search_again If true, this is a repeat search (partial matching).
|
1579 |
+
* @param bool $no_terms If true, no search term is used.
|
1580 |
+
* @param string $query_join The MySQL JOIN clause.
|
1581 |
+
* @param string $query_restrictions The MySQL query restrictions.
|
1582 |
+
*
|
1583 |
+
* @return string The MySQL search query.
|
1584 |
+
*/
|
1585 |
+
function relevanssi_generate_search_query( string $term, bool $search_again,
|
1586 |
+
bool $no_terms, string $query_join, string $query_restrictions ) : string {
|
1587 |
+
global $relevanssi_variables;
|
1588 |
+
$relevanssi_table = $relevanssi_variables['relevanssi_table'];
|
1589 |
+
|
1590 |
+
$term_cond = relevanssi_generate_term_where( $term, $search_again, $no_terms, get_option( 'relevanssi_fuzzy' ) );
|
1591 |
+
|
1592 |
+
$content_boost = floatval( get_option( 'relevanssi_content_boost', 1 ) );
|
1593 |
+
$title_boost = floatval( get_option( 'relevanssi_title_boost' ) );
|
1594 |
+
$link_boost = floatval( get_option( 'relevanssi_link_boost' ) );
|
1595 |
+
$comment_boost = floatval( get_option( 'relevanssi_comment_boost' ) );
|
1596 |
+
|
1597 |
+
$tag = ! empty( $post_type_weights['post_tag'] ) ? $post_type_weights['post_tag'] : $relevanssi_variables['post_type_weight_defaults']['post_tag'];
|
1598 |
+
$cat = ! empty( $post_type_weights['category'] ) ? $post_type_weights['category'] : $relevanssi_variables['post_type_weight_defaults']['category'];
|
1599 |
+
|
1600 |
+
// Clean: $term is escaped, as are $query_restrictions.
|
1601 |
+
$query = "SELECT DISTINCT(relevanssi.doc), relevanssi.*, relevanssi.title * $title_boost +
|
1602 |
+
relevanssi.content * $content_boost + relevanssi.comment * $comment_boost +
|
1603 |
+
relevanssi.tag * $tag + relevanssi.link * $link_boost +
|
1604 |
+
relevanssi.author + relevanssi.category * $cat + relevanssi.excerpt +
|
1605 |
+
relevanssi.taxonomy + relevanssi.customfield + relevanssi.mysqlcolumn AS tf
|
1606 |
+
FROM $relevanssi_table AS relevanssi $query_join WHERE $term_cond $query_restrictions";
|
1607 |
+
/**
|
1608 |
+
* Filters the Relevanssi search query.
|
1609 |
+
*
|
1610 |
+
* @param string $query The Relevanssi search MySQL query.
|
1611 |
+
*/
|
1612 |
+
return apply_filters( 'relevanssi_query_filter', $query );
|
1613 |
+
}
|
1614 |
+
|
1615 |
+
/**
|
1616 |
+
* Compiles search arguments that are shared between single site search and
|
1617 |
+
* multisite search.
|
1618 |
+
*
|
1619 |
+
* @param WP_Query $query The WP_Query that has the parameters.
|
1620 |
+
*
|
1621 |
+
* @return array The compiled search parameters.
|
1622 |
+
*/
|
1623 |
+
function relevanssi_compile_common_args( $query ) {
|
1624 |
+
$admin_search = isset( $query->query_vars['relevanssi_admin_search'] ) ? true : false;
|
1625 |
+
$include_attachments = $query->query_vars['include_attachments'] ?? '';
|
1626 |
+
|
1627 |
+
$by_date = '';
|
1628 |
+
if ( ! empty( $query->query_vars['by_date'] ) ) {
|
1629 |
+
if ( preg_match( '/\d+[hdmyw]/', $query->query_vars['by_date'] ) ) {
|
1630 |
+
// Accepted format is digits followed by h, d, m, y, or w.
|
1631 |
+
$by_date = $query->query_vars['by_date'];
|
1632 |
+
}
|
1633 |
+
}
|
1634 |
+
|
1635 |
+
$order = $query->query_vars['order'] ?? null;
|
1636 |
+
$orderby = $query->query_vars['orderby'] ?? null;
|
1637 |
+
|
1638 |
+
$operator = '';
|
1639 |
+
if ( function_exists( 'relevanssi_set_operator' ) ) {
|
1640 |
+
$operator = relevanssi_set_operator( $query );
|
1641 |
+
$operator = strtoupper( $operator );
|
1642 |
+
}
|
1643 |
+
if ( ! in_array( $operator, array( 'OR', 'AND' ), true ) ) {
|
1644 |
+
$operator = get_option( 'relevanssi_implicit_operator' );
|
1645 |
+
}
|
1646 |
+
|
1647 |
+
$sentence = false;
|
1648 |
+
if ( isset( $query->query_vars['sentence'] ) && ! empty( $query->query_vars['sentence'] ) ) {
|
1649 |
+
$sentence = true;
|
1650 |
+
}
|
1651 |
+
|
1652 |
+
$meta_query = relevanssi_meta_query_from_query_vars( $query );
|
1653 |
+
$date_query = relevanssi_wp_date_query_from_query_vars( $query );
|
1654 |
+
|
1655 |
+
$post_type = false;
|
1656 |
+
if ( isset( $query->query_vars['post_type'] ) && 'any' !== $query->query_vars['post_type'] ) {
|
1657 |
+
$post_type = $query->query_vars['post_type'];
|
1658 |
+
}
|
1659 |
+
if ( isset( $query->query_vars['post_types'] ) && 'any' !== $query->query_vars['post_types'] ) {
|
1660 |
+
$post_type = $query->query_vars['post_types'];
|
1661 |
+
}
|
1662 |
+
|
1663 |
+
$post_status = false;
|
1664 |
+
if ( isset( $query->query_vars['post_status'] ) && 'any' !== $query->query_vars['post_status'] ) {
|
1665 |
+
$post_status = $query->query_vars['post_status'];
|
1666 |
+
}
|
1667 |
+
|
1668 |
+
return array(
|
1669 |
+
'orderby' => $orderby,
|
1670 |
+
'order' => $order,
|
1671 |
+
'operator' => $operator,
|
1672 |
+
'admin_search' => $admin_search,
|
1673 |
+
'include_attachments' => $include_attachments,
|
1674 |
+
'by_date' => $by_date,
|
1675 |
+
'sentence' => $sentence,
|
1676 |
+
'meta_query' => $meta_query,
|
1677 |
+
'date_query' => $date_query,
|
1678 |
+
'post_type' => $post_type,
|
1679 |
+
'post_status' => $post_status,
|
1680 |
+
);
|
1681 |
+
}
|
1682 |
+
|
1683 |
+
function relevanssi_add_include_matches( &$matches, $include, $params ) {
|
1684 |
+
if ( count( $include['posts'] ) < 1 && count( $include['items'] ) < 1 ) {
|
1685 |
+
return;
|
1686 |
+
}
|
1687 |
+
|
1688 |
+
global $wpdb, $relevanssi_variables;
|
1689 |
+
$relevanssi_table = $relevanssi_variables['relevanssi_table'];
|
1690 |
+
|
1691 |
+
$term_cond = relevanssi_generate_term_where( $params['term'], $params['search_again'], $params['no_terms'] );
|
1692 |
+
$content_boost = floatval( get_option( 'relevanssi_content_boost', 1 ) ); // Default value, because this option was added late.
|
1693 |
+
$title_boost = floatval( get_option( 'relevanssi_title_boost' ) );
|
1694 |
+
$link_boost = floatval( get_option( 'relevanssi_link_boost' ) );
|
1695 |
+
$comment_boost = floatval( get_option( 'relevanssi_comment_boost' ) );
|
1696 |
+
$tag = $relevanssi_variables['post_type_weight_defaults']['post_tag'];
|
1697 |
+
$cat = $relevanssi_variables['post_type_weight_defaults']['category'];
|
1698 |
+
|
1699 |
+
if ( ! empty( $post_type_weights['post_tagged_with_post_tag'] ) ) {
|
1700 |
+
$tag = $post_type_weights['post_tagged_with_post_tag'];
|
1701 |
+
}
|
1702 |
+
if ( ! empty( $post_type_weights['post_tagged_with_category'] ) ) {
|
1703 |
+
$cat = $post_type_weights['post_tagged_with_category'];
|
1704 |
+
}
|
1705 |
+
|
1706 |
+
if ( count( $include['posts'] ) > 0 ) {
|
1707 |
+
$existing_ids = array();
|
1708 |
+
foreach ( $matches as $match ) {
|
1709 |
+
$existing_ids[] = $match->doc;
|
1710 |
+
}
|
1711 |
+
$existing_ids = array_keys( array_flip( $existing_ids ) );
|
1712 |
+
$added_post_ids = array_diff( array_keys( $include['posts'] ), $existing_ids );
|
1713 |
+
if ( count( $added_post_ids ) > 0 ) {
|
1714 |
+
$offset = 0;
|
1715 |
+
$slice_length = 20;
|
1716 |
+
$total_ids = count( $added_post_ids );
|
1717 |
+
do {
|
1718 |
+
$current_slice = array_slice( $added_post_ids, $offset, $slice_length );
|
1719 |
+
$post_ids_to_add = implode( ',', $current_slice );
|
1720 |
+
if ( ! empty( $post_ids_to_add ) ) {
|
1721 |
+
$query = "SELECT relevanssi.*, relevanssi.title * $title_boost +
|
1722 |
+
relevanssi.content * $content_boost + relevanssi.comment * $comment_boost +
|
1723 |
+
relevanssi.tag * $tag + relevanssi.link * $link_boost +
|
1724 |
+
relevanssi.author + relevanssi.category * $cat + relevanssi.excerpt +
|
1725 |
+
relevanssi.taxonomy + relevanssi.customfield + relevanssi.mysqlcolumn AS tf
|
1726 |
+
FROM $relevanssi_table AS relevanssi WHERE relevanssi.doc IN ($post_ids_to_add)
|
1727 |
+
AND $term_cond";
|
1728 |
+
|
1729 |
+
// Clean: no unescaped user inputs.
|
1730 |
+
$matches_to_add = $wpdb->get_results( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
1731 |
+
$matches = array_merge( $matches, $matches_to_add );
|
1732 |
+
}
|
1733 |
+
$offset += $slice_length;
|
1734 |
+
} while ( $offset <= $total_ids );
|
1735 |
+
}
|
1736 |
+
}
|
1737 |
+
if ( count( $include['items'] ) > 0 ) {
|
1738 |
+
$existing_items = array();
|
1739 |
+
foreach ( $matches as $match ) {
|
1740 |
+
if ( 0 !== intval( $match->item ) ) {
|
1741 |
+
$existing_items[] = $match->item;
|
1742 |
+
}
|
1743 |
+
}
|
1744 |
+
$existing_items = array_keys( array_flip( $existing_items ) );
|
1745 |
+
$items_to_add = implode( ',', array_diff( array_keys( $include['items'] ), $existing_items ) );
|
1746 |
+
|
1747 |
+
if ( ! empty( $items_to_add ) ) {
|
1748 |
+
$query = "SELECT relevanssi.*, relevanssi.title * $title_boost +
|
1749 |
+
relevanssi.content * $content_boost + relevanssi.comment * $comment_boost +
|
1750 |
+
relevanssi.tag * $tag + relevanssi.link * $link_boost +
|
1751 |
+
relevanssi.author + relevanssi.category * $cat + relevanssi.excerpt +
|
1752 |
+
relevanssi.taxonomy + relevanssi.customfield + relevanssi.mysqlcolumn AS tf
|
1753 |
+
FROM $relevanssi_table AS relevanssi WHERE relevanssi.item IN ($items_to_add)
|
1754 |
+
AND $term_cond";
|
1755 |
+
|
1756 |
+
// Clean: no unescaped user inputs.
|
1757 |
+
$matches_to_add = $wpdb->get_results( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
1758 |
+
$matches = array_merge( $matches, $matches_to_add );
|
1759 |
+
}
|
1760 |
+
}
|
1761 |
+
}
|
1762 |
+
|
1763 |
+
/**
|
1764 |
+
* Figures out the low and high boundaries for the search query.
|
1765 |
+
*
|
1766 |
+
* The low boundary defaults to 0. If the search is paged, the low boundary is
|
1767 |
+
* calculated from the page number and posts_per_page value.
|
1768 |
+
*
|
1769 |
+
* The high boundary defaults to the low boundary + post_per_page, but if no
|
1770 |
+
* posts_per_page is set or it's -1, the high boundary is the number of posts
|
1771 |
+
* found. Also if the high boundary is higher than the number of posts found,
|
1772 |
+
* it's set there.
|
1773 |
+
*
|
1774 |
+
* If an offset is defined, both boundaries are offset with the value.
|
1775 |
+
*
|
1776 |
+
* @param WP_Query $query The WP Query object.
|
1777 |
+
*
|
1778 |
+
* @return array An array with the low boundary first, the high boundary second.
|
1779 |
+
*/
|
1780 |
+
function relevanssi_get_boundaries( $query ) : array {
|
1781 |
+
$hits_count = $query->found_posts;
|
1782 |
+
|
1783 |
+
if ( isset( $query->query_vars['paged'] ) && $query->query_vars['paged'] > 0 ) {
|
1784 |
+
$search_low_boundary = ( $query->query_vars['paged'] - 1 ) * $query->query_vars['posts_per_page'];
|
1785 |
+
} else {
|
1786 |
+
$search_low_boundary = 0;
|
1787 |
+
}
|
1788 |
+
|
1789 |
+
if ( ! isset( $query->query_vars['posts_per_page'] ) || -1 === $query->query_vars['posts_per_page'] ) {
|
1790 |
+
$search_high_boundary = $hits_count;
|
1791 |
+
} else {
|
1792 |
+
$search_high_boundary = $search_low_boundary + $query->query_vars['posts_per_page'] - 1;
|
1793 |
+
}
|
1794 |
+
|
1795 |
+
if ( isset( $query->query_vars['offset'] ) && $query->query_vars['offset'] > 0 ) {
|
1796 |
+
$search_high_boundary += $query->query_vars['offset'];
|
1797 |
+
$search_low_boundary += $query->query_vars['offset'];
|
1798 |
+
}
|
1799 |
+
|
1800 |
+
if ( $search_high_boundary > $hits_count ) {
|
1801 |
+
$search_high_boundary = $hits_count;
|
1802 |
+
}
|
1803 |
+
|
1804 |
+
return array( $search_low_boundary, $search_high_boundary );
|
1805 |
+
}
|
1806 |
+
|
1807 |
+
/**
|
1808 |
+
* Returns a ID=>parent object from post ID.
|
1809 |
+
*
|
1810 |
+
* @param int $post_id The post ID.
|
1811 |
+
*
|
1812 |
+
* @return object An object with the post ID in ->ID and post parent in
|
1813 |
+
* ->post_parent.
|
1814 |
+
*/
|
1815 |
+
function relevanssi_generate_post_parent( int $post_id ) {
|
1816 |
+
$object = new StdClass();
|
1817 |
+
$object->ID = $post_id;
|
1818 |
+
$object->post_parent = wp_get_post_parent_id( $post_id );
|
1819 |
+
return $object;
|
1820 |
+
}
|
1821 |
+
|
1822 |
+
/**
|
1823 |
+
* Returns a ID=>type object from post ID.
|
1824 |
+
*
|
1825 |
+
* @param string $post_id The post ID.
|
1826 |
+
*
|
1827 |
+
* @return object An object with the post ID in ->ID, object type in ->type and
|
1828 |
+
* (possibly) term taxonomy in ->taxonomy and post type name in ->name.
|
1829 |
+
*/
|
1830 |
+
function relevanssi_generate_id_type( string $post_id ) {
|
1831 |
+
$object = new StdClass();
|
1832 |
+
if ( 'u_' === substr( $post_id, 0, 2 ) ) {
|
1833 |
+
$object->ID = intval( substr( $post_id, 2 ) );
|
1834 |
+
$object->type = 'user';
|
1835 |
+
} elseif ( '**' === substr( $post_id, 0, 2 ) ) {
|
1836 |
+
list( , $taxonomy, $id ) = explode( '**', $post_id );
|
1837 |
+
$object->ID = $id;
|
1838 |
+
$object->type = 'term';
|
1839 |
+
$object->taxonomy = $taxonomy;
|
1840 |
+
} elseif ( 'p_' === substr( $post_id, 0, 2 ) ) {
|
1841 |
+
$object->ID = intval( substr( $post_id, 2 ) );
|
1842 |
+
$object->type = 'post_type';
|
1843 |
+
$object->name = relevanssi_get_post_type_by_id( $object->ID );
|
1844 |
+
} else {
|
1845 |
+
$object->ID = $post_id;
|
1846 |
+
$object->type = 'post';
|
1847 |
+
}
|
1848 |
+
return $object;
|
1849 |
+
}
|
lib/tabs/searching-tab.php
CHANGED
@@ -57,7 +57,11 @@ function relevanssi_searching_tab() {
|
|
57 |
$orfallback_visibility = '';
|
58 |
}
|
59 |
|
60 |
-
|
|
|
|
|
|
|
|
|
61 |
?>
|
62 |
|
63 |
<table class="form-table" role="presentation">
|
@@ -322,7 +326,7 @@ function relevanssi_searching_tab() {
|
|
322 |
<?php esc_html_e( 'Throttle searches.', 'relevanssi' ); ?>
|
323 |
</label>
|
324 |
</fieldset>
|
325 |
-
<?php if ( $docs_count < 1000 ) { ?>
|
326 |
<p class="description important"><?php esc_html_e( "Your database is so small that you don't need to enable this.", 'relevanssi' ); ?></p>
|
327 |
<?php } ?>
|
328 |
<p class="description"><?php esc_html_e( 'If this option is checked, Relevanssi will limit search results to at most 500 results per term. This will improve performance, but may cause some relevant documents to go unfound. See Help for more details.', 'relevanssi' ); ?></p>
|
57 |
$orfallback_visibility = '';
|
58 |
}
|
59 |
|
60 |
+
if ( ! $throttle ) {
|
61 |
+
$docs_count = $wpdb->get_var( 'SELECT COUNT(DISTINCT doc) FROM ' . $relevanssi_variables['relevanssi_table'] . ' WHERE doc != -1' ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared
|
62 |
+
} else {
|
63 |
+
$docs_count = null;
|
64 |
+
}
|
65 |
?>
|
66 |
|
67 |
<table class="form-table" role="presentation">
|
326 |
<?php esc_html_e( 'Throttle searches.', 'relevanssi' ); ?>
|
327 |
</label>
|
328 |
</fieldset>
|
329 |
+
<?php if ( $docs_count && $docs_count < 1000 ) { ?>
|
330 |
<p class="description important"><?php esc_html_e( "Your database is so small that you don't need to enable this.", 'relevanssi' ); ?></p>
|
331 |
<?php } ?>
|
332 |
<p class="description"><?php esc_html_e( 'If this option is checked, Relevanssi will limit search results to at most 500 results per term. This will improve performance, but may cause some relevant documents to go unfound. See Help for more details.', 'relevanssi' ); ?></p>
|
lib/utils.php
CHANGED
@@ -20,6 +20,17 @@ function get_relevanssi_taxonomy_walker() {
|
|
20 |
return new Relevanssi_Taxonomy_Walker();
|
21 |
}
|
22 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
/**
|
24 |
* Wraps the relevanssi_mb_trim() function so that it can be used as a callback
|
25 |
* for array_walk().
|
@@ -155,6 +166,10 @@ function relevanssi_get_an_object( $source ) {
|
|
155 |
// Convert from post ID to post.
|
156 |
$object = relevanssi_get_post_object( $source );
|
157 |
$format = 'id';
|
|
|
|
|
|
|
|
|
158 |
} elseif ( ! isset( $source->post_content ) ) {
|
159 |
// Convert from id=>parent to post.
|
160 |
$object = relevanssi_get_post_object( $source->ID );
|
@@ -299,6 +314,44 @@ function relevanssi_get_post( $post_id, int $blog_id = -1 ) {
|
|
299 |
return $post;
|
300 |
}
|
301 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
302 |
/**
|
303 |
* Returns an object based on ID.
|
304 |
*
|
@@ -621,6 +674,36 @@ function relevanssi_return_id_parent( $post_object ) {
|
|
621 |
return $id_parent_object;
|
622 |
}
|
623 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
624 |
/**
|
625 |
* Returns "off".
|
626 |
*
|
@@ -645,6 +728,8 @@ function relevanssi_return_off() {
|
|
645 |
function relevanssi_return_value( $post, string $return_value ) {
|
646 |
if ( 'id' === $return_value ) {
|
647 |
return $post->ID;
|
|
|
|
|
648 |
} elseif ( 'id=>parent' === $return_value ) {
|
649 |
return relevanssi_return_id_parent( $post );
|
650 |
}
|
@@ -747,8 +832,9 @@ function relevanssi_strip_invisibles( $text ) {
|
|
747 |
* Strips tags from contents, keeping the allowed tags.
|
748 |
*
|
749 |
* The allowable tags are read from the relevanssi_excerpt_allowable_tags
|
750 |
-
* option.
|
751 |
-
*
|
|
|
752 |
*
|
753 |
* @see relevanssi_strip_invisibles
|
754 |
*
|
@@ -761,7 +847,19 @@ function relevanssi_strip_tags( $content ) {
|
|
761 |
$content = strval( $content );
|
762 |
}
|
763 |
$content = relevanssi_strip_invisibles( $content );
|
764 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
765 |
return strip_tags(
|
766 |
$content,
|
767 |
get_option( 'relevanssi_excerpt_allowable_tags', '' )
|
@@ -799,7 +897,7 @@ function relevanssi_stripos( $haystack, $needle, int $offset = 0 ) {
|
|
799 |
$needle_regex = str_replace(
|
800 |
array( '?', '*' ),
|
801 |
array( '.', '.*' ),
|
802 |
-
$needle
|
803 |
);
|
804 |
$pos_found = false;
|
805 |
while ( ! $pos_found ) {
|
20 |
return new Relevanssi_Taxonomy_Walker();
|
21 |
}
|
22 |
|
23 |
+
/**
|
24 |
+
* Adds quotes around a string.
|
25 |
+
*
|
26 |
+
* @param string $string The string.
|
27 |
+
*
|
28 |
+
* @return string The string with quotes around it.
|
29 |
+
*/
|
30 |
+
function relevanssi_add_quotes( $string ) {
|
31 |
+
return '"' . $string . '"';
|
32 |
+
}
|
33 |
+
|
34 |
/**
|
35 |
* Wraps the relevanssi_mb_trim() function so that it can be used as a callback
|
36 |
* for array_walk().
|
166 |
// Convert from post ID to post.
|
167 |
$object = relevanssi_get_post_object( $source );
|
168 |
$format = 'id';
|
169 |
+
} elseif ( isset( $source->type ) ) {
|
170 |
+
// Convert from id=>type to post.
|
171 |
+
$object = relevanssi_get_post_object( $source->ID );
|
172 |
+
$format = 'id=>type';
|
173 |
} elseif ( ! isset( $source->post_content ) ) {
|
174 |
// Convert from id=>parent to post.
|
175 |
$object = relevanssi_get_post_object( $source->ID );
|
314 |
return $post;
|
315 |
}
|
316 |
|
317 |
+
/**
|
318 |
+
* Fetches post meta value for a large group of posts with just one query.
|
319 |
+
*
|
320 |
+
* This function can be used to reduce the number of database queries. Instead
|
321 |
+
* of looping through an array of posts and calling get_post_meta() for each
|
322 |
+
* individual post, you can get all the values with this function with just one
|
323 |
+
* database query.
|
324 |
+
*
|
325 |
+
* @param array $post_ids An array of post IDs.
|
326 |
+
* @param string $field The name of the field.
|
327 |
+
*
|
328 |
+
* @return array An array of post_id, meta_value pairs.
|
329 |
+
*/
|
330 |
+
function relevanssi_get_post_meta_for_all_posts( array $post_ids, string $field ) : array {
|
331 |
+
global $wpdb;
|
332 |
+
|
333 |
+
$post_ids_string = implode( ',', $post_ids );
|
334 |
+
$meta_values = array();
|
335 |
+
|
336 |
+
if ( $post_ids_string ) {
|
337 |
+
$meta_values = $wpdb->get_results(
|
338 |
+
$wpdb->prepare(
|
339 |
+
"SELECT post_id, meta_value FROM $wpdb->postmeta
|
340 |
+
WHERE meta_key = %s
|
341 |
+
AND post_id IN ( $post_ids_string )", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared
|
342 |
+
$field
|
343 |
+
)
|
344 |
+
);
|
345 |
+
}
|
346 |
+
|
347 |
+
$results = array();
|
348 |
+
foreach ( $meta_values as $row ) {
|
349 |
+
$results[ $row->post_id ] = $row->meta_value;
|
350 |
+
}
|
351 |
+
|
352 |
+
return $results;
|
353 |
+
}
|
354 |
+
|
355 |
/**
|
356 |
* Returns an object based on ID.
|
357 |
*
|
674 |
return $id_parent_object;
|
675 |
}
|
676 |
|
677 |
+
/**
|
678 |
+
* Returns an ID=>type object from a post (or a term, or a user).
|
679 |
+
*
|
680 |
+
* @param WP_Post|WP_Term|WP_User $post_object The source object.
|
681 |
+
*
|
682 |
+
* @return object An object with the attributes ID and type set. Type is
|
683 |
+
* 'post', 'user', 'term' or 'post_type'. For terms, also fills in 'taxonomy',
|
684 |
+
* for post types 'name'.
|
685 |
+
*/
|
686 |
+
function relevanssi_return_id_type( $post_object ) {
|
687 |
+
$id_type_object = new stdClass();
|
688 |
+
|
689 |
+
if ( isset( $post_object->ID ) ) {
|
690 |
+
$id_type_object->ID = $post_object->ID;
|
691 |
+
$id_type_object->type = 'post';
|
692 |
+
} elseif ( isset( $post_object->term_id ) ) {
|
693 |
+
$id_type_object->ID = $post_object->term_id;
|
694 |
+
$id_type_object->type = 'term';
|
695 |
+
$id_type_object->taxonomy = $post_object->taxonomy;
|
696 |
+
} elseif ( isset( $post_object->user_id ) ) {
|
697 |
+
$id_type_object->ID = $post_object->user_id;
|
698 |
+
$id_type_object->type = 'user';
|
699 |
+
} else {
|
700 |
+
$id_type_object->ID = 0;
|
701 |
+
$id_type_object->post_parent = 0;
|
702 |
+
}
|
703 |
+
|
704 |
+
return $id_type_object;
|
705 |
+
}
|
706 |
+
|
707 |
/**
|
708 |
* Returns "off".
|
709 |
*
|
728 |
function relevanssi_return_value( $post, string $return_value ) {
|
729 |
if ( 'id' === $return_value ) {
|
730 |
return $post->ID;
|
731 |
+
} elseif ( 'id=>type' === $return_value ) {
|
732 |
+
return relevanssi_return_id_type( $post );
|
733 |
} elseif ( 'id=>parent' === $return_value ) {
|
734 |
return relevanssi_return_id_parent( $post );
|
735 |
}
|
832 |
* Strips tags from contents, keeping the allowed tags.
|
833 |
*
|
834 |
* The allowable tags are read from the relevanssi_excerpt_allowable_tags
|
835 |
+
* option. Relevanssi also adds extra spaces after some tags to make sure words
|
836 |
+
* are not stuck together after the tags are removed. The function also removes
|
837 |
+
* invisible content.
|
838 |
*
|
839 |
* @see relevanssi_strip_invisibles
|
840 |
*
|
847 |
$content = strval( $content );
|
848 |
}
|
849 |
$content = relevanssi_strip_invisibles( $content );
|
850 |
+
|
851 |
+
$space_tags = array(
|
852 |
+
'/(<\/?p.*?>)/',
|
853 |
+
'/(<\/?br.*?>)/',
|
854 |
+
'/(<\/?h[1-6].*?>)/',
|
855 |
+
'/(<\/?div.*?>)/',
|
856 |
+
'/(<\/?blockquote.*?>)/',
|
857 |
+
'/(<\/?hr.*?>)/',
|
858 |
+
'/(<\/?li.*?>)/',
|
859 |
+
'/(<img.*?>)/',
|
860 |
+
);
|
861 |
+
|
862 |
+
$content = preg_replace( $space_tags, '$1 ', $content );
|
863 |
return strip_tags(
|
864 |
$content,
|
865 |
get_option( 'relevanssi_excerpt_allowable_tags', '' )
|
897 |
$needle_regex = str_replace(
|
898 |
array( '?', '*' ),
|
899 |
array( '.', '.*' ),
|
900 |
+
preg_quote( $needle, '/' )
|
901 |
);
|
902 |
$pos_found = false;
|
903 |
while ( ! $pos_found ) {
|
readme.txt
CHANGED
@@ -3,9 +3,9 @@ Contributors: msaari
|
|
3 |
Donate link: https://www.relevanssi.com/buy-premium/
|
4 |
Tags: search, relevance, better search, product search, woocommerce search
|
5 |
Requires at least: 4.9
|
6 |
-
Tested up to: 5.7
|
7 |
Requires PHP: 7.0
|
8 |
-
Stable tag: 4.
|
9 |
License: GPLv2 or later
|
10 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
11 |
|
@@ -131,6 +131,21 @@ Each document database is full of useless words. All the little words that appea
|
|
131 |
* John Calahan for extensive 4.0 beta testing.
|
132 |
|
133 |
== Changelog ==
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
134 |
= 4.12.5 =
|
135 |
* Changed behaviour: `relevanssi_excerpt_custom_field_content` now gets the post ID and list of custom field names as a parameter.
|
136 |
* Minor fix: Makes sure Relevanssi options are not wiped when the free version is deleted while Premium is active.
|
@@ -205,31 +220,10 @@ Each document database is full of useless words. All the little words that appea
|
|
205 |
* Minor fix: Relevanssi had an unnecessary index on the `doc` column in the `wp_relevanssi` database table. It is now removed to save space. Thanks to Matthew Wang.
|
206 |
* Minor fix: Improved Oxygen Builder support makes sure `ct_builder_shortcodes` custom field is always indexed.
|
207 |
|
208 |
-
= 4.9.1 =
|
209 |
-
* Changed behaviour: The `relevanssi_excerpt_part` filter hook now gets the post ID as a second parameter. The documentation for the filter has been fixed to match actual use: this filter is applied to the excerpt part after the highlighting and the ellipsis have been added.
|
210 |
-
* Changed behaviour: The `relevanssi_index_custom_fields` filter hook is no longer used when determining which custom fields are used for phrase searching. If you have a use case where this change matters, please contact us.
|
211 |
-
* Minor fix: The `relevanssi_excerpt` filter hook was removed in 4.9.0. It is now restored and behaves the way it did before.
|
212 |
-
* Minor fix: Avoids undefined variable warnings from the Pretty Links compatibility code.
|
213 |
-
* Minor fix: The Oxygen Builder compatibility has been improved. Now shortcodes in Oxygen Builder content are expanded, if that setting is enabled in Relevanssi settings.
|
214 |
-
|
215 |
-
= 4.9.0 =
|
216 |
-
* New feature: There's now a "Debugging" tab in the Relevanssi settings, letting you see how the Relevanssi index sees posts. This is familiar to Premium users, but is now available in the free version as well.
|
217 |
-
* New feature: The SEO Framework plugin is now supported and posts set excluded from the search in SEO Framework settings will be excluded from the index.
|
218 |
-
* New feature: There's a new option, "Expand highlights". Enabling it makes Relevanssi expand partial-word highlights to cover the full word. This is useful when doing partial matching and when using a stemmer.
|
219 |
-
* New feature: New filter hook `relevanssi_excerpt_part` allows you to modify the excerpt parts before they are combined together. This doesn't do much in the free version.
|
220 |
-
* New feature: Improved compatibility with Oxygen Builder. Relevanssi automatically indexes the Oxygen Builder content and cleans it up. New filter hooks `relevanssi_oxygen_section_filters` and `relevanssi_oxygen_section_content` allow easier filtering of Oxygen content to eg. remove unwanted sections.
|
221 |
-
* Changed behaviour: The "Uncheck this for non-ASCII highlights" option has been removed. Highlights are now done in a slightly different way that should work in all cases, including for example Cyrillic text, thus this option is no longer necessary.
|
222 |
-
* Minor fix: Fixes phrase searching using non-US alphabet.
|
223 |
-
* Minor fix: Relevanssi would break admin searching for hierarchical post types. This is now fixed, Relevanssi won't do that anymore.
|
224 |
-
* Minor fix: Relevanssi indexing now survives better shortcodes that change the global `$post`.
|
225 |
-
* Minor fix: Warnings about missing `relevanssi_update_counts` function are now removed.
|
226 |
-
* Minor fix: Paid Membership Pro support now takes notice of the "filter queries" setting.
|
227 |
-
* Minor fix: OR logic didn't work correctly when two phrases both had the same word (for example "freedom of speech" and "free speech"). The search would always be an AND search in those cases. That has been fixed.
|
228 |
-
* Minor fix: Relevanssi no longer blocks the Pretty Links admin page search.
|
229 |
-
* Minor fix: The "Respect 'exclude_from_search'" setting did not work if no post type parameter was included in the search parameters.
|
230 |
-
* Minor fix: The category inclusion and exclusion setting checkboxes on the Searching tab didn't work. The setting was saved, but the checkboxes wouldn't appear.
|
231 |
-
|
232 |
== Upgrade notice ==
|
|
|
|
|
|
|
233 |
= 4.12.5 =
|
234 |
* Fixes minor bugs.
|
235 |
|
@@ -261,10 +255,4 @@ Each document database is full of useless words. All the little words that appea
|
|
261 |
* Corrects the multilingual stopwords and synonyms.
|
262 |
|
263 |
= 4.10.0 =
|
264 |
-
* Adds support for multilingual stopwords and synonyms.
|
265 |
-
|
266 |
-
= 4.9.1 =
|
267 |
-
* Bug fixing, better Oxygen Builder compatibility.
|
268 |
-
|
269 |
-
= 4.9.0 =
|
270 |
-
* New debugging feature, lots of minor fixes.
|
3 |
Donate link: https://www.relevanssi.com/buy-premium/
|
4 |
Tags: search, relevance, better search, product search, woocommerce search
|
5 |
Requires at least: 4.9
|
6 |
+
Tested up to: 5.7.1
|
7 |
Requires PHP: 7.0
|
8 |
+
Stable tag: 4.13.0
|
9 |
License: GPLv2 or later
|
10 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
11 |
|
131 |
* John Calahan for extensive 4.0 beta testing.
|
132 |
|
133 |
== Changelog ==
|
134 |
+
= 4.13.0 =
|
135 |
+
* New feature: New filter hook `relevanssi_phrase` filters each phrase before it's used in the MySQL query.
|
136 |
+
* New feature: Relevanssi can now add Google-style missing term lists to the search results. You can either use the `%missing%` tag in the search results breakdown settings, or you can create your own code: the missing terms are also in `$post->missing_terms`. Relevanssi Premium will also add "Must have" links when there's just one missing term.
|
137 |
+
* New feature: New filter hook `relevanssi_missing_terms_tag` controls which tag is used to wrap the missing terms.
|
138 |
+
* New feature: New filter hook `relevanssi_missing_terms_template` can be used to filter the template used to display the missing terms.
|
139 |
+
* New feature: New function `relevanssi_get_post_meta_for_all_posts()` can be used to fetch particular meta field for a number of posts with just one query.
|
140 |
+
* New feature: New filter hook `relevanssi_post_author` lets you filter the post author display_name before it is indexed.
|
141 |
+
* Changed behaviour: `relevanssi_strip_tags()` used to add spaces between HTML tags before stripping them. It no longer does that, but instead adds a space after specific list of tags (p, br, h1-h6, div, blockquote, hr, li, img) to avoid words being stuck to each other in excerpts.
|
142 |
+
* Changed behaviour: Relevanssi now indexes the contents of Oxygen Builder PHP & HTML code blocks.
|
143 |
+
* Changed behaviour: Relevanssi now handles synonyms inside phrases differently. If the new filter hook `relevanssi_phrase_synonyms` returns `true` (default value), synonyms create a new phrase (with synonym 'dog=hound', phrase `"dog biscuits"` becomes `"dog biscuits" "hound biscuits"`). If the value is `false`, synonyms inside phrases are ignored.
|
144 |
+
* Minor fix: Warnings when creating excerpts with search terms that contain a slash were removed.
|
145 |
+
* Minor fix: Better Ninja Tables compatibility to avoid problems with lightbox images.
|
146 |
+
* Minor fix: Relevanssi did not work well in the Media Library grid view. Relevanssi is now blocked there. If you need Relevanssi in Media Library searches, use the list view.
|
147 |
+
* Minor fix: Relevanssi excerpt creation didn't work correctly when numerical search terms were used.
|
148 |
+
|
149 |
= 4.12.5 =
|
150 |
* Changed behaviour: `relevanssi_excerpt_custom_field_content` now gets the post ID and list of custom field names as a parameter.
|
151 |
* Minor fix: Makes sure Relevanssi options are not wiped when the free version is deleted while Premium is active.
|
220 |
* Minor fix: Relevanssi had an unnecessary index on the `doc` column in the `wp_relevanssi` database table. It is now removed to save space. Thanks to Matthew Wang.
|
221 |
* Minor fix: Improved Oxygen Builder support makes sure `ct_builder_shortcodes` custom field is always indexed.
|
222 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
223 |
== Upgrade notice ==
|
224 |
+
= 4.13.0 =
|
225 |
+
* Lots of new features and bug fixes.
|
226 |
+
|
227 |
= 4.12.5 =
|
228 |
* Fixes minor bugs.
|
229 |
|
255 |
* Corrects the multilingual stopwords and synonyms.
|
256 |
|
257 |
= 4.10.0 =
|
258 |
+
* Adds support for multilingual stopwords and synonyms.
|
|
|
|
|
|
|
|
|
|
|
|
relevanssi.php
CHANGED
@@ -13,7 +13,7 @@
|
|
13 |
* Plugin Name: Relevanssi
|
14 |
* Plugin URI: https://www.relevanssi.com/
|
15 |
* Description: This plugin replaces WordPress search with a relevance-sorting search.
|
16 |
-
* Version: 4.
|
17 |
* Author: Mikko Saari
|
18 |
* Author URI: http://www.mikkosaari.fi/
|
19 |
* Text Domain: relevanssi
|
@@ -67,7 +67,7 @@ $relevanssi_variables['database_version'] = 6;
|
|
67 |
$relevanssi_variables['file'] = __FILE__;
|
68 |
$relevanssi_variables['plugin_dir'] = plugin_dir_path( __FILE__ );
|
69 |
$relevanssi_variables['plugin_basename'] = plugin_basename( __FILE__ );
|
70 |
-
$relevanssi_variables['plugin_version'] = '4.
|
71 |
|
72 |
require_once 'lib/admin-ajax.php';
|
73 |
require_once 'lib/common.php';
|
13 |
* Plugin Name: Relevanssi
|
14 |
* Plugin URI: https://www.relevanssi.com/
|
15 |
* Description: This plugin replaces WordPress search with a relevance-sorting search.
|
16 |
+
* Version: 4.13.0
|
17 |
* Author: Mikko Saari
|
18 |
* Author URI: http://www.mikkosaari.fi/
|
19 |
* Text Domain: relevanssi
|
67 |
$relevanssi_variables['file'] = __FILE__;
|
68 |
$relevanssi_variables['plugin_dir'] = plugin_dir_path( __FILE__ );
|
69 |
$relevanssi_variables['plugin_basename'] = plugin_basename( __FILE__ );
|
70 |
+
$relevanssi_variables['plugin_version'] = '4.13.0';
|
71 |
|
72 |
require_once 'lib/admin-ajax.php';
|
73 |
require_once 'lib/common.php';
|