function relevanssi_search($q, $tax_query = NULL, $relation = NULL, $post_query = array(), $meta_query = array(), $expost = NULL, $post_type = NULL, $operator = "AND", $search_blogs = NULL, $author = NULL, $orderby = NULL, $order = NULL) { global $wpdb, $relevanssi_variables; $relevanssi_table = $relevanssi_variables['relevanssi_table']; $values_to_filter = array('q' => $q, 'tax_query' => $tax_query, 'relation' => $relation, 'post_query' => $post_query, 'meta_query' => $meta_query, 'expost' => $expost, 'post_type' => $post_type, 'operator' => $operator, 'search_blogs' => $search_blogs, 'author' => $author, 'orderby' => $orderby, 'order' => $order); $filtered_values = apply_filters('relevanssi_search_filters', $values_to_filter); $q = $filtered_values['q']; $tax_query = $filtered_values['tax_query']; $post_query = $filtered_values['post_query']; $meta_query = $filtered_values['meta_query']; $relation = $filtered_values['relation']; $expost = $filtered_values['expost']; $post_type = $filtered_values['post_type']; $operator = $filtered_values['operator']; $search_blogs = $filtered_values['search_blogs']; $author = $filtered_values['author']; $orderby = $filtered_values['orderby']; $order = $filtered_values['order']; $hits = array(); $o_tax_query = $tax_query; $o_relation = $relation; $o_post_query = $post_query; $o_meta_query = $meta_query; $o_expost = $expost; $o_post_type = $post_type; $o_author = $author; $o_operator = $operator; $o_search_blogs = $search_blogs; $o_orderby = $orderby; $o_order = $order; $query_restrictions = ""; if (!isset($relation)) { $relation = "or"; } $relation = strtolower($relation); $term_tax_id = array(); $term_tax_ids = array(); $not_term_tax_ids = array(); $and_term_tax_ids = array(); if (is_array($tax_query)) { foreach ($tax_query as $row) { if ($row['field'] == 'id' || $row['field'] == 'term_id') { $id = $row['terms']; $term_id = $id; if (is_array($id)) { $id = implode(',', $id); } $term_tax_id = $wpdb->get_col("SELECT tt.term_taxonomy_id\n\t\t\t\t\tFROM {$wpdb->terms} AS t, {$wpdb->term_taxonomy} AS tt\n\t\t\t\t\tWHERE tt.term_id = t.term_id AND tt.taxonomy = '" . $row['taxonomy'] . "' AND t.term_id IN ({$id})"); } if ($row['field'] == 'slug') { $slug = $row['terms']; if (is_array($slug)) { $slugs = array(); $term_id = array(); foreach ($slug as $t_slug) { $term = get_term_by('slug', $t_slug, $row['taxonomy']); $term_id[] = $term->term_id; $slugs[] = "'{$t_slug}'"; } $slug = implode(',', $slugs); } else { $term = get_term_by('slug', $slug, $row['taxonomy']); $term_id = $term->term_id; $slug = "'{$slug}'"; } $term_tax_id = $wpdb->get_col("SELECT tt.term_taxonomy_id\n\t\t\t\t\tFROM {$wpdb->terms} AS t, {$wpdb->term_taxonomy} AS tt\n\t\t\t\t\tWHERE tt.term_id = t.term_id AND tt.taxonomy = '" . $row['taxonomy'] . "' AND t.slug IN ({$slug})"); } if (!isset($row['include_children']) || $row['include_children'] == true) { if (!is_array($term_id)) { $term_id = array($term_id); } foreach ($term_id as $t_id) { $kids = get_term_children($t_id, $row['taxonomy']); foreach ($kids as $kid) { $term = get_term_by('id', $kid, $row['taxonomy']); $term_tax_id[] = relevanssi_get_term_tax_id('id', $kid, $row['taxonomy']); } } } $term_tax_id = array_unique($term_tax_id); if (!empty($term_tax_id)) { $n = count($term_tax_id); $term_tax_id = implode(',', $term_tax_id); $tq_operator = 'IN'; if (isset($row['operator'])) { $tq_operator = strtoupper($row['operator']); } if ($tq_operator != 'IN' && $tq_operator != 'NOT IN' && $tq_operator != 'AND') { $tq_operator = 'IN'; } if ($relation == 'and') { if ($tq_operator == 'AND') { $query_restrictions .= " AND relevanssi.doc IN (\n\t\t\t\t\t\t\tSELECT ID FROM {$wpdb->posts} WHERE 1=1 \n\t\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t\tSELECT COUNT(1) \n\t\t\t\t\t\t\t\tFROM {$wpdb->term_relationships} AS tr\n\t\t\t\t\t\t\t\tWHERE tr.term_taxonomy_id IN ({$term_tax_id}) \n\t\t\t\t\t\t\t\tAND tr.object_id = {$wpdb->posts}.ID ) = {$n}\n\t\t\t\t\t\t\t)"; } else { $query_restrictions .= " AND relevanssi.doc {$tq_operator} (SELECT DISTINCT(tr.object_id) FROM {$wpdb->term_relationships} AS tr\n\t\t\t\t\t\tWHERE tr.term_taxonomy_id IN ({$term_tax_id}))"; } } else { if ($tq_operator == 'IN') { $term_tax_ids[] = $term_tax_id; } if ($tq_operator == 'NOT IN') { $not_term_tax_ids[] = $term_tax_id; } if ($tq_operator == 'AND') { $and_term_tax_ids[] = $term_tax_id; } } } else { global $wp_query; $wp_query->is_category = false; } } if ($relation == 'or') { $term_tax_ids = array_unique($term_tax_ids); if (count($term_tax_ids) > 0) { $term_tax_ids = implode(',', $term_tax_ids); $query_restrictions .= " AND relevanssi.doc IN (SELECT DISTINCT(tr.object_id) FROM {$wpdb->term_relationships} AS tr\n\t\t\t \tWHERE tr.term_taxonomy_id IN ({$term_tax_ids}))"; } if (count($not_term_tax_ids) > 0) { $not_term_tax_ids = implode(',', $not_term_tax_ids); $query_restrictions .= " AND relevanssi.doc NOT IN (SELECT DISTINCT(tr.object_id) FROM {$wpdb->term_relationships} AS tr\n\t\t\t \tWHERE tr.term_taxonomy_id IN ({$not_term_tax_ids}))"; } if (count($and_term_tax_ids) > 0) { $and_term_tax_ids = implode(',', $and_term_tax_ids); $n = count(explode(',', $and_term_tax_ids)); $query_restrictions .= " AND relevanssi.doc IN (\n\t\t\t\t\tSELECT ID FROM {$wpdb->posts} AS posts WHERE 1=1 \n\t\t\t\t\tAND (\n\t\t\t\t\t\tSELECT COUNT(1) \n\t\t\t\t\t\tFROM {$wpdb->term_relationships} AS tr\n\t\t\t\t\t\tWHERE tr.term_taxonomy_id IN ({$and_term_tax_ids}) \n\t\t\t\t\t\tAND tr.object_id = {$wpdb->posts}.ID ) = {$n}\n\t\t\t\t\t)"; } } } if (is_array($post_query)) { if (!empty($post_query['in'])) { $posts = implode(',', $post_query['in']); $query_restrictions .= " AND relevanssi.doc IN ({$posts})"; } if (!empty($post_query['not in'])) { $posts = implode(',', $post_query['not in']); $query_restrictions .= " AND relevanssi.doc NOT IN ({$posts})"; } } if (is_array($meta_query)) { isset($meta_query['relation']) ? $meta_relation = strtoupper($meta_query['relation']) : ($meta_relation = strtoupper(apply_filters('relevanssi_default_meta_query_relation', 'AND'))); $meta_query_restrictions = ""; foreach ($meta_query as $array_key => $meta) { if ($array_key === 'relation') { continue; } if (!empty($meta['key'])) { $key = "postmeta.meta_key = '" . $meta['key'] . "'"; } else { $key = ''; } isset($meta['compare']) ? $compare = strtoupper($meta['compare']) : ($compare = '='); if (isset($meta['type'])) { if (strtoupper($meta['type']) == 'NUMERIC') { $meta['type'] = "SIGNED"; } $meta_value = "CAST(postmeta.meta_value AS " . $meta['type'] . ")"; } else { $meta_value = 'postmeta.meta_value'; } if ($compare == 'BETWEEN' || $compare == 'NOT BETWEEN') { if (!is_array($meta['value'])) { continue; } if (count($meta['value']) < 2) { continue; } $compare == 'BETWEEN' ? $compare = "IN" : ($compare = "NOT IN"); $low_value = $meta['value'][0]; $high_value = $meta['value'][1]; !empty($key) ? $and = " AND " : ($and = ""); $meta_query_restrictions .= " {$meta_relation} relevanssi.doc {$compare} (\n\t\t\t\t\tSELECT DISTINCT(postmeta.post_id) FROM {$wpdb->postmeta} AS postmeta\n\t\t\t\t\tWHERE {$key} {$and} {$meta_value} BETWEEN {$low_value} AND {$high_value})"; } else { if ($compare == 'EXISTS' || $compare == 'NOT EXISTS') { $compare == 'EXISTS' ? $compare = "IN" : ($compare = "NOT IN"); $meta_query_restrictions .= " {$meta_relation} relevanssi.doc {$compare} (\n\t\t\t\t\tSELECT DISTINCT(postmeta.post_id) FROM {$wpdb->postmeta} AS postmeta\n\t\t\t\t\tWHERE {$key})"; } else { if ($compare == 'IN' || $compare == 'NOT IN') { if (!is_array($meta['value'])) { continue; } $values = array(); foreach ($meta['value'] as $value) { $values[] = "'{$value}'"; } $values = implode(',', $values); !empty($key) ? $and = " AND " : ($and = ""); $meta_query_restrictions .= " {$meta_relation} relevanssi.doc IN (\n\t\t\t\t\tSELECT DISTINCT(postmeta.post_id) FROM {$wpdb->postmeta} AS postmeta\n\t\t\t\t\tWHERE {$key} {$and} {$meta_value} {$compare} ({$values}))"; } else { isset($meta['value']) ? $value = " {$meta_value} " . $meta['compare'] . " '" . $meta['value'] . "' " : ($value = ''); !empty($key) && !empty($value) ? $and = " AND " : ($and = ""); if (empty($key) && empty($and) && empty($value)) { // do nothing } else { $meta_query_restrictions .= " {$meta_relation} relevanssi.doc IN (\n\t\t\t\t\t\tSELECT DISTINCT(postmeta.post_id) FROM {$wpdb->postmeta} AS postmeta\n\t\t\t\t\t\tWHERE {$key} {$and} {$value})"; } } } } } if ($meta_relation == 'OR') { $meta_query_restrictions = substr($meta_query_restrictions, 3); // strip the first OR $meta_query_restrictions = "AND (" . $meta_query_restrictions . ") "; } $query_restrictions .= $meta_query_restrictions; } if (!$post_type && get_option('relevanssi_respect_exclude') == 'on') { if (function_exists('get_post_types')) { $pt_1 = get_post_types(array('exclude_from_search' => '0')); $pt_2 = get_post_types(array('exclude_from_search' => false)); $post_type = implode(',', array_merge($pt_1, $pt_2)); } } if ($post_type) { if ($post_type == -1) { $post_type = null; } // Facetious sets post_type to -1 if not selected if (!is_array($post_type)) { $post_types = explode(',', $post_type); } else { $post_types = $post_type; } $post_type = count($post_types) ? implode(',', array_fill(1, count($post_types), "'%s'")) : 'NULL'; } //Added by OdditY: //Exclude Post_IDs (Pages) for non-admin search -> $postex = ''; if ($expost) { if ($expost != "") { $aexpids = explode(",", $expost); foreach ($aexpids as $exid) { $exid = esc_sql(trim($exid, ' -')); $postex .= " AND relevanssi.doc !='{$exid}'"; } } } // <- OdditY End $remove_stopwords = true; $phrases = relevanssi_recognize_phrases($q); if (function_exists('relevanssi_recognize_negatives')) { $negative_terms = relevanssi_recognize_negatives($q); } else { $negative_terms = false; } if (function_exists('relevanssi_recognize_positives')) { $positive_terms = relevanssi_recognize_positives($q); } else { $positive_terms = false; } $terms = relevanssi_tokenize($q, $remove_stopwords); if (count($terms) < 1) { // Tokenizer killed all the search terms. return $hits; } $terms = array_keys($terms); // don't care about tf in query if ($negative_terms) { $terms = array_diff($terms, $negative_terms); if (count($terms) < 1) { return $hits; } } $D = $wpdb->get_var("SELECT COUNT(DISTINCT(relevanssi.doc)) FROM {$relevanssi_table} AS relevanssi"); $total_hits = 0; $title_matches = array(); $tag_matches = array(); $comment_matches = array(); $link_matches = array(); $body_matches = array(); $scores = array(); $term_hits = array(); $fuzzy = get_option('relevanssi_fuzzy'); if ($expost) { //added by OdditY $query_restrictions .= $postex; } if (function_exists('relevanssi_negatives_positives')) { $query_restrictions .= relevanssi_negatives_positives($negative_terms, $positive_terms, $relevanssi_table); } if (!empty($author)) { $author_in = array(); $author_not_in = array(); foreach ($author as $id) { if ($id > 0) { $author_in[] = $id; } else { $author_not_in[] = abs($id); } } if (count($author_in) > 0) { $authors = implode(',', $author_in); $query_restrictions .= " AND relevanssi.doc IN (SELECT DISTINCT(posts.ID) FROM {$wpdb->posts} AS posts\n\t\t\t WHERE posts.post_author IN ({$authors}))"; } if (count($author_not_in) > 0) { $authors = implode(',', $author_not_in); $query_restrictions .= " AND relevanssi.doc NOT IN (SELECT DISTINCT(posts.ID) FROM {$wpdb->posts} AS posts\n\t\t\t WHERE posts.post_author IN ({$authors}))"; } } if ($post_type) { // the -1 is there to get user profiles and category pages $query_restrictions .= $wpdb->prepare(" AND ((relevanssi.doc IN (SELECT DISTINCT(posts.ID) FROM {$wpdb->posts} AS posts\n\t\t\tWHERE posts.post_type IN ({$post_type}))) OR (doc = -1))", $post_types); } if ($phrases) { $query_restrictions .= " AND relevanssi.doc IN ({$phrases})"; } if (isset($_REQUEST['by_date'])) { $n = $_REQUEST['by_date']; $u = substr($n, -1, 1); switch ($u) { case 'h': $unit = "HOUR"; break; case 'd': $unit = "DAY"; break; case 'm': $unit = "MONTH"; break; case 'y': $unit = "YEAR"; break; case 'w': $unit = "WEEK"; break; default: $unit = "DAY"; } $n = preg_replace('/[hdmyw]/', '', $n); if (is_numeric($n)) { $query_restrictions .= " AND relevanssi.doc IN (SELECT DISTINCT(posts.ID) FROM {$wpdb->posts} AS posts\n\t\t\t\tWHERE posts.post_date > DATE_SUB(NOW(), INTERVAL {$n} {$unit}))"; } } $query_restrictions = apply_filters('relevanssi_where', $query_restrictions); // Charles St-Pierre $query_join = apply_filters('relevanssi_join', ''); $no_matches = true; if ("always" == $fuzzy) { $o_term_cond = apply_filters('relevanssi_fuzzy_query', "(relevanssi.term LIKE '#term#%' OR relevanssi.term_reverse LIKE CONCAT(REVERSE('#term#'), '%')) "); } else { $o_term_cond = " relevanssi.term = '#term#' "; } $post_type_weights = get_option('relevanssi_post_type_weights'); if (function_exists('relevanssi_get_recency_bonus')) { list($recency_bonus, $recency_cutoff_date) = relevanssi_get_recency_bonus(); } else { $recency_bonus = false; $recency_cutoff_date = false; } $min_length = get_option('relevanssi_min_word_length'); $search_again = false; $title_boost = floatval(get_option('relevanssi_title_boost')); $link_boost = floatval(get_option('relevanssi_link_boost')); $comment_boost = floatval(get_option('relevanssi_comment_boost')); $include_these_posts = array(); do { foreach ($terms as $term) { $term = trim($term); // numeric search terms will start with a space if (strlen($term) < $min_length) { continue; } $term = esc_sql(like_escape($term)); $term_cond = str_replace('#term#', $term, $o_term_cond); !empty($post_type_weights['post_tag']) ? $tag = $post_type_weights['post_tag'] : ($tag = $relevanssi_variables['post_type_weight_defaults']['post_tag']); !empty($post_type_weights['category']) ? $cat = $post_type_weights['category'] : ($cat = $relevanssi_variables['post_type_weight_defaults']['category']); $query = "SELECT relevanssi.*, relevanssi.title * {$title_boost} + relevanssi.content + relevanssi.comment * {$comment_boost} + relevanssi.tag * {$tag} + relevanssi.link * {$link_boost} + relevanssi.author + relevanssi.category * {$cat} + relevanssi.excerpt + relevanssi.taxonomy + relevanssi.customfield + relevanssi.mysqlcolumn AS tf \n\t\t\t\t\t FROM {$relevanssi_table} AS relevanssi {$query_join} WHERE {$term_cond} {$query_restrictions}"; $query = apply_filters('relevanssi_query_filter', $query); $matches = $wpdb->get_results($query); if (count($matches) < 1) { continue; } else { $no_matches = false; if (count($include_these_posts) > 0) { $post_ids_to_add = implode(',', array_keys($include_these_posts)); $query = "SELECT relevanssi.*, relevanssi.title * {$title_boost} + relevanssi.content + relevanssi.comment * {$comment_boost} + relevanssi.tag * {$tag} + relevanssi.link * {$link_boost} + relevanssi.author + relevanssi.category * {$cat} + relevanssi.excerpt + relevanssi.taxonomy + relevanssi.customfield + relevanssi.mysqlcolumn AS tf \n\t\t\t\t\t\t FROM {$relevanssi_table} AS relevanssi WHERE relevanssi.doc IN ({$post_ids_to_add}) AND {$term_cond}"; $matches_to_add = $wpdb->get_results($query); $matches = array_merge($matches, $matches_to_add); } } relevanssi_populate_array($matches); global $relevanssi_post_types; $total_hits += count($matches); $query = "SELECT COUNT(DISTINCT(relevanssi.doc)) FROM {$relevanssi_table} AS relevanssi {$query_join} WHERE {$term_cond} {$query_restrictions}"; $query = apply_filters('relevanssi_df_query_filter', $query); $df = $wpdb->get_var($query); if ($df < 1 && "sometimes" == $fuzzy) { $query = "SELECT COUNT(DISTINCT(relevanssi.doc)) FROM {$relevanssi_table} AS relevanssi {$query_join}\n\t\t\t\t\tWHERE (relevanssi.term LIKE '{$term}%' OR relevanssi.term_reverse LIKE CONCAT(REVERSE('{$term}), %')) {$query_restrictions}"; $query = apply_filters('relevanssi_df_query_filter', $query); $df = $wpdb->get_var($query); } $idf = log($D + 1 / (1 + $df)); $idf = $idf * $idf; foreach ($matches as $match) { if ('user' == $match->type) { $match->doc = 'u_' . $match->item; } if ('taxonomy' == $match->type) { $match->doc = 't_' . $match->item; } if (isset($match->taxonomy_detail)) { $match->taxonomy_score = 0; $match->taxonomy_detail = unserialize($match->taxonomy_detail); if (is_array($match->taxonomy_detail)) { foreach ($match->taxonomy_detail as $tax => $count) { if ($tax == 'post_tag') { $match->tag = $count; } if (empty($post_type_weights[$tax])) { $match->taxonomy_score += $count * 1; } else { $match->taxonomy_score += $count * $post_type_weights[$tax]; } } } } $match->tf = $match->title * $title_boost + $match->content + $match->comment * $comment_boost + $match->link * $link_boost + $match->author + $match->excerpt + $match->taxonomy_score + $match->customfield + $match->mysqlcolumn; $term_hits[$match->doc][$term] = $match->title + $match->content + $match->comment + $match->tag + $match->link + $match->author + $match->category + $match->excerpt + $match->taxonomy + $match->customfield + $match->mysqlcolumn; $match->weight = $match->tf * $idf; if ($recency_bonus) { $post = relevanssi_get_post($match->doc); if (strtotime($post->post_date) > $recency_cutoff_date) { $match->weight = $match->weight * $recency_bonus['bonus']; } } $body_matches[$match->doc] = $match->content; $title_matches[$match->doc] = $match->title; $link_matches[$match->doc] = $match->link; $tag_matches[$match->doc] = $match->tag; $comment_matches[$match->doc] = $match->comment; isset($relevanssi_post_types[$match->doc]) ? $type = $relevanssi_post_types[$match->doc] : ($type = null); if (!empty($post_type_weights[$type])) { $match->weight = $match->weight * $post_type_weights[$type]; } $match = apply_filters('relevanssi_match', $match, $idf); if ($match->weight == 0) { continue; } // the filters killed the match $post_ok = true; $post_ok = apply_filters('relevanssi_post_ok', $post_ok, $match->doc); if ($post_ok) { $doc_terms[$match->doc][$term] = true; // count how many terms are matched to a doc isset($doc_weight[$match->doc]) ? $doc_weight[$match->doc] += $match->weight : ($doc_weight[$match->doc] = $match->weight); isset($scores[$match->doc]) ? $scores[$match->doc] += $match->weight : ($scores[$match->doc] = $match->weight); if (is_numeric($match->doc)) { // this is to weed out taxonomies and users (t_XXX, u_XXX) $include_these_posts[$match->doc] = true; } } } } if (!isset($doc_weight)) { $no_matches = true; } if ($no_matches) { if ($search_again) { // no hits even with fuzzy search! $search_again = false; } else { if ("sometimes" == $fuzzy) { $search_again = true; $o_term_cond = "(term LIKE '%#term#' OR term LIKE '#term#%') "; } } } else { $search_again = false; } } while ($search_again); $strip_stops = true; $temp_terms_without_stops = array_keys(relevanssi_tokenize(implode(' ', $terms), $strip_stops)); $terms_without_stops = array(); foreach ($temp_terms_without_stops as $temp_term) { if (strlen($temp_term) >= $min_length) { array_push($terms_without_stops, $temp_term); } } $total_terms = count($terms_without_stops); if (isset($doc_weight)) { $doc_weight = apply_filters('relevanssi_results', $doc_weight); } if (isset($doc_weight) && count($doc_weight) > 0) { arsort($doc_weight); $i = 0; foreach ($doc_weight as $doc => $weight) { if (count($doc_terms[$doc]) < $total_terms && $operator == "AND") { // AND operator in action: // doc didn't match all terms, so it's discarded continue; } $hits[intval($i)] = relevanssi_get_post($doc); $hits[intval($i)]->relevance_score = round($weight, 2); $i++; } } if (count($hits) < 1) { if ($operator == "AND" and get_option('relevanssi_disable_or_fallback') != 'on') { $return = relevanssi_search($q, $o_tax_query, $o_relation, $o_post_query, $o_meta_query, $o_expost, $o_post_type, "OR", $o_search_blogs, $o_author); extract($return); } } global $wp; $default_order = get_option('relevanssi_default_orderby', 'relevance'); if (empty($orderby)) { $orderby = $default_order; } // the sorting function checks for non-existing keys, cannot whitelist here if (empty($order)) { $order = 'desc'; } $order = strtolower($order); $order_accepted_values = array('asc', 'desc'); if (!in_array($order, $order_accepted_values)) { $order = 'desc'; } if ($orderby != 'relevance') { relevanssi_object_sort($hits, $orderby, $order); } $return = array('hits' => $hits, 'body_matches' => $body_matches, 'title_matches' => $title_matches, 'tag_matches' => $tag_matches, 'comment_matches' => $comment_matches, 'scores' => $scores, 'term_hits' => $term_hits, 'query' => $q, 'link_matches' => $link_matches); return $return; }
function relevanssi_search($q, $cat = NULL, $excat = NULL, $expost = NULL, $post_type = NULL, $taxonomy = NULL, $taxonomy_term = NULL, $operator = "AND") { global $relevanssi_table, $wpdb; $hits = array(); $custom_cat = NULL; $o_cat = $cat; $o_excat = $excat; $o_expost = $expost; $o_post_type = $post_type; $o_taxonomy = $taxonomy; $o_taxonomy_term = $taxonomy_term; if ("custom" == $cat) { $custom_field = "custom"; $post_ids = array(); $results = $wpdb->get_results("SELECT post_id FROM $wpdb->postmeta WHERE meta_key='$custom_field'"); foreach ($results as $row) { $post_ids[] = $row->post_id; } $custom_cat = implode(",", $post_ids); $cat = ""; } else if ($cat) { $cats = explode(",", $cat); $inc_term_tax_ids = array(); $ex_term_tax_ids = array(); foreach ($cats as $t_cat) { $exclude = false; if ($t_cat < 0) { // Negative category, ie. exclusion $exclude = true; $t_cat = substr($t_cat, 1); // strip the - sign. } $t_cat = $wpdb->escape($t_cat); $term_tax_id = $wpdb->get_var("SELECT term_taxonomy_id FROM $wpdb->term_taxonomy WHERE term_id=$t_cat"); if ($term_tax_id) { $exclude ? $ex_term_tax_ids[] = $term_tax_id : $inc_term_tax_ids[] = $term_tax_id; } } $cat = implode(",", $inc_term_tax_ids); $excat_temp = implode(",", $ex_term_tax_ids); } if ($excat) { $excats = explode(",", $excat); $term_tax_ids = array(); foreach ($excats as $t_cat) { $t_cat = $wpdb->escape(trim($t_cat, ' -')); $term_tax_id = $wpdb->get_var("SELECT term_taxonomy_id FROM $wpdb->term_taxonomy WHERE term_id=$t_cat"); if ($term_tax_id) { $term_tax_ids[] = $term_tax_id; } } $excat = implode(",", $term_tax_ids); } if (isset($excat_temp)) { $excat .= $excat_temp; } if (isset($taxonomy)) { $term_tax_id = null; $term_tax_id = $wpdb->get_var("SELECT term_taxonomy_id FROM $wpdb->terms JOIN $wpdb->term_taxonomy USING(`term_id`) WHERE `slug` LIKE '$taxonomy_term' AND `taxonomy` LIKE '$taxonomy'"); if ($term_tax_id) { $taxonomy = $term_tax_id; } else { $taxonomy = null; } } if ($post_type) { if (!is_array($post_type)) { $post_types = explode(',', $post_type); } else { $post_types = $post_type; } $pt_array = array(); foreach ($post_types as $pt) { $pt = "'" . trim(mysql_real_escape_string($pt)) . "'"; array_push($pt_array, $pt); } $post_type = implode(",", $pt_array); } //Added by OdditY: //Exclude Post_IDs (Pages) for non-admin search -> if ($expost) { if ($expost != "") { $aexpids = explode(",",$expost); foreach ($aexpids as $exid){ $exid = $wpdb->escape(trim($exid, ' -')); $postex .= " AND doc !='$exid'"; } } } // <- OdditY End $remove_stopwords = false; $phrases = relevanssi_recognize_phrases($q); $terms = relevanssi_tokenize($q, $remove_stopwords); if (count($terms) < 1) { // Tokenizer killed all the search terms. return $hits; } $terms = array_keys($terms); // don't care about tf in query $D = $wpdb->get_var("SELECT COUNT(DISTINCT(doc)) FROM $relevanssi_table"); $total_hits = 0; $title_matches = array(); $tag_matches = array(); $comment_matches = array(); $body_matches = array(); $scores = array(); $term_hits = array(); $fuzzy = get_option('relevanssi_fuzzy'); $query_restrictions = ""; if ($expost) { //added by OdditY $query_restrictions .= $postex; } if ($cat) { $query_restrictions .= " AND doc IN (SELECT DISTINCT(object_id) FROM $wpdb->term_relationships WHERE term_taxonomy_id IN ($cat))"; } if ($excat) { $query_restrictions .= " AND doc NOT IN (SELECT DISTINCT(object_id) FROM $wpdb->term_relationships WHERE term_taxonomy_id IN ($excat))"; } if ($post_type) { $query_restrictions .= " AND doc IN (SELECT DISTINCT(ID) FROM $wpdb->posts WHERE post_type IN ($post_type))"; } if ($phrases) { $query_restrictions .= " AND doc IN ($phrases)"; } if ($custom_cat) { $query_restrictions .= " AND doc IN ($custom_cat)"; } if ($taxonomy) { $query_restrictions .= " AND doc IN (SELECT DISTINCT(object_id) FROM $wpdb->term_relationships WHERE term_taxonomy_id IN ($taxonomy))"; } if (isset($_REQUEST['by_date'])) { $n = $_REQUEST['by_date']; $u = substr($n, -1, 1); switch ($u) { case 'h': $unit = "HOUR"; break; case 'd': $unit = "DAY"; break; case 'm': $unit = "MONTH"; break; case 'y': $unit = "YEAR"; break; case 'w': $unit = "WEEK"; break; default: $unit = "DAY"; } $n = preg_replace('/[hdmyw]/', '', $n); if (is_numeric($n)) { $query_restrictions .= " AND doc IN (SELECT DISTINCT(ID) FROM $wpdb->posts WHERE post_date > DATE_SUB(NOW(), INTERVAL $n $unit))"; } } $query_restrictions = apply_filters('relevanssi_where', $query_restrictions); // Charles St-Pierre foreach ($terms as $term) { $term = $wpdb->escape(like_escape($term)); if ("always" == $fuzzy) { $term_cond = "(term LIKE '%$term' OR term LIKE '$term%') "; } else { $term_cond = " term = '$term' "; } $query = "SELECT doc, term, tf, title FROM $relevanssi_table WHERE $term_cond $query_restrictions"; $matches = $wpdb->get_results($query); if (count($matches) < 1 && "sometimes" == $fuzzy) { $query = "SELECT doc, term, tf, title FROM $relevanssi_table WHERE (term LIKE '$term%' OR term LIKE '%$term') $query_restrictions"; $matches = $wpdb->get_results($query); } $total_hits += count($matches); $query = "SELECT COUNT(DISTINCT(doc)) FROM $relevanssi_table WHERE $term_cond $query_restrictions"; $df = $wpdb->get_var($query); if ($df < 1 && "sometimes" == $fuzzy) { $query = "SELECT COUNT(DISTINCT(doc)) FROM $relevanssi_table WHERE (term LIKE '%$term' OR term LIKE '$term%') $query_restrictions"; $df = $wpdb->get_var($query); } $title_boost = floatval(get_option('relevanssi_title_boost')); $tag_boost = floatval(get_option('relevanssi_tag_boost')); $comment_boost = floatval(get_option('relevanssi_comment_boost')); $idf = log($D / (1 + $df)); // $doc_terms_temp = array(); foreach ($matches as $match) { $weight = $match->tf * $idf; if (!isset($term_hits[$match->doc][$term])) { $term_hits[$match->doc][$term] = 0; } switch ($match->title) { case "1": $weight = $weight * $title_boost; isset($title_matches[$match->doc]) ? $title_matches[$match->doc] += $match->tf : $title_matches[$match->doc] = $match->tf; $term_hits[$match->doc][$term] += $match->tf; break; case "2": $weight = $weight * $tag_boost; isset($tag_matches[$match->doc]) ? $tag_matches[$match->doc] += $match->tf : $tag_matches[$match->doc] = $match->tf; $term_hits[$match->doc][$term] += $match->tf; break; case "3": $weight = $weight * $comment_boost; isset($comment_matches[$match->doc]) ? $comment_matches[$match->doc] += $match->tf : $comment_matches[$match->doc] = $match->tf; $term_hits[$match->doc][$term] += $match->tf; break; default: isset($body_matches[$match->doc]) ? $body_matches[$match->doc] += $match->tf : $body_matches[$match->doc] = $match->tf; $term_hits[$match->doc][$term] += $match->tf; } $doc_terms[$match->doc][$term] = true; // count how many terms are matched to a doc isset($doc_weight[$match->doc]) ? $doc_weight[$match->doc] += $weight : $doc_weight[$match->doc] = $weight; isset($scores[$match->doc]) ? $scores[$match->doc] += $weight : $scores[$match->doc] = $weight; } } $total_terms = count($terms); if (isset($doc_weight) && count($doc_weight) > 0) { arsort($doc_weight); $i = 0; foreach ($doc_weight as $doc => $weight) { if (count($doc_terms[$doc]) < $total_terms && $operator == "AND") { // AND operator in action: // doc didn't match all terms, so it's discarded continue; } $status = get_post_status($doc); $post_ok = true; if ('private' == $status) { $post_ok = false; if (function_exists('awp_user_can')) { // Role-Scoper $current_user = wp_get_current_user(); $post_ok = awp_user_can('read_post', $doc, $current_user->ID); } else { // Basic WordPress version $type = get_post_type($doc); $cap = 'read_private_' . $type . 's'; if (current_user_can($cap)) { $post_ok = true; } } } if ($post_ok) $hits[intval($i++)] = get_post($doc); } } if (count($hits) < 1) { if ($operator == "AND" AND get_option('relevanssi_disable_or_fallback') != 'on') { $return = relevanssi_search($q, $o_cat, $o_excat, $o_expost, $o_post_type, $o_taxonomy, $o_taxonomy_term, "OR"); extract($return); } } global $wp; isset($wp->query_vars["orderby"]) ? $orderby = $wp->query_vars["orderby"] : $orderby = 'relevance'; isset($wp->query_vars["order"]) ? $order = $wp->query_vars["order"] : $order = 'desc'; if ($orderby != 'relevance') objectSort($hits, $orderby, $order); $return = array('hits' => $hits, 'body_matches' => $body_matches, 'title_matches' => $title_matches, 'tag_matches' => $tag_matches, 'comment_matches' => $comment_matches, 'scores' => $scores, 'term_hits' => $term_hits); return $return; }
function relevanssi_search($args) { global $wpdb, $relevanssi_variables; $relevanssi_table = $relevanssi_variables['relevanssi_table']; $filtered_args = apply_filters('relevanssi_search_filters', $args); extract($filtered_args); $hits = array(); $query_restrictions = ""; if (!isset($tax_query_relation)) { $tax_query_relation = "or"; } $tax_query_relation = strtolower($tax_query_relation); $term_tax_id = array(); $term_tax_ids = array(); $not_term_tax_ids = array(); $and_term_tax_ids = array(); if (is_array($tax_query)) { foreach ($tax_query as $row) { if ($row['field'] == 'slug') { $slug = $row['terms']; $numeric_slugs = array(); $slug_in = null; if (is_array($slug)) { $slugs = array(); $term_id = array(); foreach ($slug as $t_slug) { $term = get_term_by('slug', $t_slug, $row['taxonomy']); if (!$term && is_numeric($t_slug)) { $numeric_slugs[] = "'{$t_slug}'"; } else { $t_slug = sanitize_title($t_slug); $term_id[] = $term->term_id; $slugs[] = "'{$t_slug}'"; } } if (!empty($slugs)) { $slug_in = implode(',', $slugs); } } else { $term = get_term_by('slug', $slug, $row['taxonomy']); if (!$term && is_numeric($slug)) { $numeric_slugs[] = $slug; } else { $term_id = $term->term_id; $slug_in = "'{$slug}'"; } } if (!empty($slug_in)) { $row_taxonomy = sanitize_text_field($row['taxonomy']); $tt_q = "SELECT tt.term_taxonomy_id\n\t\t\t\t\t\t \tFROM {$wpdb->term_taxonomy} AS tt\n\t\t\t\t\t\t \tLEFT JOIN {$wpdb->terms} AS t ON (tt.term_id=t.term_id)\n\t\t\t\t\t\t \tWHERE tt.taxonomy = '{$row_taxonomy}' AND t.slug IN ({$slug_in})"; // Clean: $row_taxonomy is sanitized, each slug in $slug_in is sanitized $term_tax_id = $wpdb->get_col($tt_q); } if (!empty($numeric_slugs)) { $row['field'] = 'id'; } } if ($row['field'] == 'id' || $row['field'] == 'term_id') { $id = $row['terms']; $term_id = $id; if (is_array($id)) { $numeric_values = array(); foreach ($id as $t_id) { if (is_numeric($t_id)) { $numeric_values[] = $t_id; } } $id = implode(',', $numeric_values); } $row_taxonomy = sanitize_text_field($row['taxonomy']); $tt_q = "SELECT tt.term_taxonomy_id\n\t\t\t\t \tFROM {$wpdb->term_taxonomy} AS tt\n\t\t\t\t \tLEFT JOIN {$wpdb->terms} AS t ON (tt.term_id=t.term_id)\n\t\t\t\t \tWHERE tt.taxonomy = '{$row_taxonomy}' AND t.term_id IN ({$id})"; // Clean: $row_taxonomy is sanitized, $id is checked to be numeric $id_term_tax_id = $wpdb->get_col($tt_q); if (!empty($term_tax_id) && is_array($term_tax_id)) { $term_tax_id = array_unique(array_merge($term_tax_id, $id_term_tax_id)); } else { $term_tax_id = $id_term_tax_id; } } if (!isset($row['include_children']) || $row['include_children'] == true) { if (!is_array($term_id)) { $term_id = array($term_id); } foreach ($term_id as $t_id) { $kids = get_term_children($t_id, $row['taxonomy']); foreach ($kids as $kid) { $term = get_term_by('id', $kid, $row['taxonomy']); $term_tax_id[] = relevanssi_get_term_tax_id('id', $kid, $row['taxonomy']); } } } $term_tax_id = array_unique($term_tax_id); if (!empty($term_tax_id)) { $n = count($term_tax_id); $term_tax_id = implode(',', $term_tax_id); $tq_operator = 'IN'; if (isset($row['operator'])) { $tq_operator = strtoupper($row['operator']); } if ($tq_operator != 'IN' && $tq_operator != 'NOT IN' && $tq_operator != 'AND') { $tq_operator = 'IN'; } if ($tax_query_relation == 'and') { if ($tq_operator == 'AND') { $query_restrictions .= " AND relevanssi.doc IN (\n\t\t\t\t\t\t\tSELECT ID FROM {$wpdb->posts} WHERE 1=1 \n\t\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t\tSELECT COUNT(1) \n\t\t\t\t\t\t\t\tFROM {$wpdb->term_relationships} AS tr\n\t\t\t\t\t\t\t\tWHERE tr.term_taxonomy_id IN ({$term_tax_id}) \n\t\t\t\t\t\t\t\tAND tr.object_id = {$wpdb->posts}.ID ) = {$n}\n\t\t\t\t\t\t\t)"; // Clean: $term_tax_id and $n are Relevanssi-generated } else { $query_restrictions .= " AND relevanssi.doc {$tq_operator} (SELECT DISTINCT(tr.object_id) FROM {$wpdb->term_relationships} AS tr\n\t\t\t\t\t\tWHERE tr.term_taxonomy_id IN ({$term_tax_id}))"; // Clean: all variables are Relevanssi-generated } } else { if ($tq_operator == 'IN') { $term_tax_ids[] = $term_tax_id; } if ($tq_operator == 'NOT IN') { $not_term_tax_ids[] = $term_tax_id; } if ($tq_operator == 'AND') { $and_term_tax_ids[] = $term_tax_id; } } } else { global $wp_query; $wp_query->is_category = false; } } if ($tax_query_relation == 'or') { $term_tax_ids = array_unique($term_tax_ids); if (count($term_tax_ids) > 0) { $term_tax_ids = implode(',', $term_tax_ids); $query_restrictions .= " AND relevanssi.doc IN (SELECT DISTINCT(tr.object_id) FROM {$wpdb->term_relationships} AS tr\n\t\t\t \tWHERE tr.term_taxonomy_id IN ({$term_tax_ids}))"; // Clean: all variables are Relevanssi-generated } if (count($not_term_tax_ids) > 0) { $not_term_tax_ids = implode(',', $not_term_tax_ids); $query_restrictions .= " AND relevanssi.doc NOT IN (SELECT DISTINCT(tr.object_id) FROM {$wpdb->term_relationships} AS tr\n\t\t\t \tWHERE tr.term_taxonomy_id IN ({$not_term_tax_ids}))"; // Clean: all variables are Relevanssi-generated } if (count($and_term_tax_ids) > 0) { $and_term_tax_ids = implode(',', $and_term_tax_ids); $n = count(explode(',', $and_term_tax_ids)); $query_restrictions .= " AND relevanssi.doc IN (\n\t\t\t\t\tSELECT ID FROM {$wpdb->posts} WHERE 1=1 \n\t\t\t\t\tAND (\n\t\t\t\t\t\tSELECT COUNT(1) \n\t\t\t\t\t\tFROM {$wpdb->term_relationships} AS tr\n\t\t\t\t\t\tWHERE tr.term_taxonomy_id IN ({$and_term_tax_ids}) \n\t\t\t\t\t\tAND tr.object_id = {$wpdb->posts}.ID ) = {$n}\n\t\t\t\t\t)"; // Clean: all variables are Relevanssi-generated } } } if (is_array($post_query)) { if (!empty($post_query['in'])) { $valid_values = array(); foreach ($post_query['in'] as $post_in_id) { if (is_numeric($post_in_id)) { $valid_values[] = $post_in_id; } } $posts = implode(',', $valid_values); if (!empty($posts)) { $query_restrictions .= " AND relevanssi.doc IN ({$posts})"; } // Clean: $posts is checked to be integers } if (!empty($post_query['not in'])) { $valid_values = array(); foreach ($post_query['not in'] as $post_not_in_id) { if (is_numeric($post_not_in_id)) { $valid_values[] = $post_not_in_id; } } $posts = implode(',', $valid_values); if (!empty($posts)) { $query_restrictions .= " AND relevanssi.doc NOT IN ({$posts})"; } // Clean: $posts is checked to be integers } } if (is_array($parent_query)) { if (!empty($parent_query['parent in'])) { $valid_values = array(); foreach ($parent_query['parent in'] as $post_in_id) { if (is_numeric($post_in_id)) { $valid_values[] = $post_in_id; } } $posts = implode(',', $valid_values); if (!empty($posts)) { $query_restrictions .= " AND relevanssi.doc IN (SELECT ID FROM {$wpdb->posts} WHERE post_parent IN ({$posts}))"; } // Clean: $posts is checked to be integers } if (!empty($parent_query['parent not in'])) { $valid_values = array(); foreach ($parent_query['parent not in'] as $post_not_in_id) { if (is_numeric($post_not_in_id)) { $valid_values[] = $post_not_in_id; } } $posts = implode(',', $valid_values); if (!empty($posts)) { $query_restrictions .= " AND relevanssi.doc NOT IN (SELECT ID FROM {$wpdb->posts} WHERE post_parent IN ({$posts}))"; } // Clean: $posts is checked to be integers } } if (is_array($meta_query)) { $meta_query_restrictions = ""; $mq_vars = array('meta_query' => $meta_query); $mq = new WP_Meta_Query(); $mq->parse_query_vars($mq_vars); $meta_sql = $mq->get_sql('post', 'relevanssi', 'doc'); $meta_join = ""; $meta_where = ""; if ($meta_sql) { $meta_join = $meta_sql['join']; $meta_where = $meta_sql['where']; } $query_restrictions .= $meta_where; } if (!empty($date_query)) { if (is_object($date_query) && method_exists($date_query, 'get_sql')) { $sql = $date_query->get_sql(); // AND ( the query itself ) $query_restrictions .= " AND relevanssi.doc IN ( SELECT DISTINCT(ID) FROM {$wpdb->posts} WHERE 1 {$sql} )"; // Clean: $sql generated by $date_query->get_sql() query } } if (!$post_type && get_option('relevanssi_respect_exclude') == 'on') { if (function_exists('get_post_types')) { $pt_1 = get_post_types(array('exclude_from_search' => '0')); $pt_2 = get_post_types(array('exclude_from_search' => false)); $post_type = implode(',', array_merge($pt_1, $pt_2)); } } if ($post_type) { if ($post_type == -1) { $post_type = null; } // Facetious sets post_type to -1 if not selected if (!is_array($post_type)) { $post_types = esc_sql(explode(',', $post_type)); } else { $post_types = esc_sql($post_type); } $post_type = count($post_types) ? "'" . implode("', '", $post_types) . "'" : 'NULL'; } if ($post_status) { if (!is_array($post_status)) { $post_statuses = esc_sql(explode(',', $post_status)); } else { $post_statuses = esc_sql($post_status); } $post_status = count($post_statuses) ? "'" . implode("', '", $post_statuses) . "'" : 'NULL'; } //Added by OdditY: //Exclude Post_IDs (Pages) for non-admin search -> $postex = ''; if (!empty($expost)) { if ($expost != "") { $aexpids = explode(",", $expost); foreach ($aexpids as $exid) { $exid = esc_sql(trim($exid, ' -')); $postex .= " AND relevanssi.doc != '{$exid}'"; // Clean: escaped } } } // <- OdditY End if ($expost) { //added by OdditY $query_restrictions .= $postex; } $remove_stopwords = true; if (function_exists('wp_encode_emoji')) { $q = wp_encode_emoji($q); } $phrases = relevanssi_recognize_phrases($q); if (function_exists('relevanssi_recognize_negatives')) { $negative_terms = relevanssi_recognize_negatives($q); } else { $negative_terms = false; } if (function_exists('relevanssi_recognize_positives')) { $positive_terms = relevanssi_recognize_positives($q); } else { $positive_terms = false; } $terms = relevanssi_tokenize($q, $remove_stopwords); if (count($terms) < 1) { // Tokenizer killed all the search terms. return $hits; } $terms = array_keys($terms); // don't care about tf in query if ($negative_terms) { $terms = array_diff($terms, $negative_terms); if (count($terms) < 1) { return $hits; } } // Go get the count from the options table, but keep running the full query if it's not available $D = get_option('relevanssi_doc_count'); if (!$D || $D < 1) { $D = $wpdb->get_var("SELECT COUNT(DISTINCT(relevanssi.doc)) FROM {$relevanssi_table} AS relevanssi"); // Clean: no external inputs update_option('relevanssi_doc_count', $D); } $total_hits = 0; $title_matches = array(); $tag_matches = array(); $comment_matches = array(); $link_matches = array(); $body_matches = array(); $category_matches = array(); $taxonomy_matches = array(); $scores = array(); $term_hits = array(); $fuzzy = get_option('relevanssi_fuzzy'); if (function_exists('relevanssi_negatives_positives')) { $query_restrictions .= relevanssi_negatives_positives($negative_terms, $positive_terms, $relevanssi_table); // Clean: escaped in the function } if (!empty($author)) { $author_in = array(); $author_not_in = array(); foreach ($author as $id) { if (!is_numeric($id)) { continue; } if ($id > 0) { $author_in[] = $id; } else { $author_not_in[] = abs($id); } } if (count($author_in) > 0) { $authors = implode(',', $author_in); $query_restrictions .= " AND relevanssi.doc IN (SELECT DISTINCT(posts.ID) FROM {$wpdb->posts} AS posts\n\t\t\t WHERE posts.post_author IN ({$authors}))"; // Clean: $authors is always just numbers } if (count($author_not_in) > 0) { $authors = implode(',', $author_not_in); $query_restrictions .= " AND relevanssi.doc NOT IN (SELECT DISTINCT(posts.ID) FROM {$wpdb->posts} AS posts\n\t\t\t WHERE posts.post_author IN ({$authors}))"; // Clean: $authors is always just numbers } } if ($post_type) { // the -1 is there to get user profiles and category pages $query_restrictions .= " AND ((relevanssi.doc IN (SELECT DISTINCT(posts.ID) FROM {$wpdb->posts} AS posts\n\t\t\tWHERE posts.post_type IN ({$post_type}))) OR (doc = -1))"; // Clean: $post_type is escaped } if ($post_status) { // the -1 is there to get user profiles and category pages $query_restrictions .= " AND ((relevanssi.doc IN (SELECT DISTINCT(posts.ID) FROM {$wpdb->posts} AS posts\n\t\t\tWHERE posts.post_status IN ({$post_status}))) OR (doc = -1))"; // Clean: $post_status is escaped } if ($phrases) { $query_restrictions .= " {$phrases}"; // Clean: $phrases is escaped earlier } if (isset($_REQUEST['by_date'])) { $n = $_REQUEST['by_date']; $u = substr($n, -1, 1); switch ($u) { case 'h': $unit = "HOUR"; break; case 'd': $unit = "DAY"; break; case 'm': $unit = "MONTH"; break; case 'y': $unit = "YEAR"; break; case 'w': $unit = "WEEK"; break; default: $unit = "DAY"; } $n = preg_replace('/[hdmyw]/', '', $n); if (is_numeric($n)) { $query_restrictions .= " AND relevanssi.doc IN (SELECT DISTINCT(posts.ID) FROM {$wpdb->posts} AS posts\n\t\t\t\tWHERE posts.post_date > DATE_SUB(NOW(), INTERVAL {$n} {$unit}))"; // Clean: $n is always numeric, $unit is Relevanssi-generated } } $query_restrictions = apply_filters('relevanssi_where', $query_restrictions); // Charles St-Pierre $query_join = ""; if (!empty($meta_join)) { $query_join = $meta_join; } $query_join = apply_filters('relevanssi_join', $query_join); $no_matches = true; if ("always" == $fuzzy) { $o_term_cond = apply_filters('relevanssi_fuzzy_query', "(relevanssi.term LIKE '#term#%' OR relevanssi.term_reverse LIKE CONCAT(REVERSE('#term#'), '%')) "); } else { $o_term_cond = " relevanssi.term = '#term#' "; } $post_type_weights = get_option('relevanssi_post_type_weights'); if (function_exists('relevanssi_get_recency_bonus')) { list($recency_bonus, $recency_cutoff_date) = relevanssi_get_recency_bonus(); } else { $recency_bonus = false; $recency_cutoff_date = false; } $min_length = get_option('relevanssi_min_word_length'); $search_again = false; $title_boost = floatval(get_option('relevanssi_title_boost')); $link_boost = floatval(get_option('relevanssi_link_boost')); $comment_boost = floatval(get_option('relevanssi_comment_boost')); $include_these_posts = array(); do { foreach ($terms as $term) { $term = trim($term); // numeric search terms will start with a space if (strlen($term) < $min_length) { continue; } $term = esc_sql($term); if (strpos($o_term_cond, 'LIKE') !== false) { // only like_escape() if necessary, otherwise _ in search terms will not work if (method_exists($wpdb, 'esc_like')) { $term = $wpdb->esc_like($term); } else { // Compatibility for pre-4.0 WordPress $term = like_escape($term); } } $term_cond = str_replace('#term#', $term, $o_term_cond); !empty($post_type_weights['post_tag']) ? $tag = $post_type_weights['post_tag'] : ($tag = $relevanssi_variables['post_type_weight_defaults']['post_tag']); !empty($post_type_weights['category']) ? $cat = $post_type_weights['category'] : ($cat = $relevanssi_variables['post_type_weight_defaults']['category']); $query = "SELECT relevanssi.*, relevanssi.title * {$title_boost} + relevanssi.content + relevanssi.comment * {$comment_boost} + relevanssi.tag * {$tag} + relevanssi.link * {$link_boost} + relevanssi.author + relevanssi.category * {$cat} + relevanssi.excerpt + relevanssi.taxonomy + relevanssi.customfield + relevanssi.mysqlcolumn AS tf \n\t\t\t\t\t FROM {$relevanssi_table} AS relevanssi {$query_join} WHERE {$term_cond} {$query_restrictions}"; // Clean: $query_restrictions is escaped, $term_cond is escaped $query = apply_filters('relevanssi_query_filter', $query); $matches = $wpdb->get_results($query); if (count($matches) < 1) { continue; } else { $no_matches = false; if (count($include_these_posts) > 0) { $post_ids_to_add = implode(',', array_keys($include_these_posts)); $existing_ids = array(); foreach ($matches as $match) { $existing_ids[] = $match->doc; } $existing_ids = implode(',', $existing_ids); $query = "SELECT relevanssi.*, relevanssi.title * {$title_boost} + relevanssi.content + relevanssi.comment * {$comment_boost} + relevanssi.tag * {$tag} + relevanssi.link * {$link_boost} + relevanssi.author + relevanssi.category * {$cat} + relevanssi.excerpt + relevanssi.taxonomy + relevanssi.customfield + relevanssi.mysqlcolumn AS tf \n\t\t\t\t\t\t FROM {$relevanssi_table} AS relevanssi WHERE relevanssi.doc IN ({$post_ids_to_add}) AND relevanssi.doc NOT IN ({$existing_ids}) AND {$term_cond}"; // Clean: no unescaped user inputs $matches_to_add = $wpdb->get_results($query); $matches = array_merge($matches, $matches_to_add); } } relevanssi_populate_array($matches); global $relevanssi_post_types; $total_hits += count($matches); $query = "SELECT COUNT(DISTINCT(relevanssi.doc)) FROM {$relevanssi_table} AS relevanssi {$query_join} WHERE {$term_cond} {$query_restrictions}"; // Clean: $query_restrictions is escaped, $term_cond is escaped $query = apply_filters('relevanssi_df_query_filter', $query); $df = $wpdb->get_var($query); if ($df < 1 && "sometimes" == $fuzzy) { $query = "SELECT COUNT(DISTINCT(relevanssi.doc)) FROM {$relevanssi_table} AS relevanssi {$query_join}\n\t\t\t\t\tWHERE (relevanssi.term LIKE '{$term}%' OR relevanssi.term_reverse LIKE CONCAT(REVERSE('{$term}), %')) {$query_restrictions}"; // Clean: $query_restrictions is escaped, $term is escaped $query = apply_filters('relevanssi_df_query_filter', $query); $df = $wpdb->get_var($query); } $idf = log($D + 1 / (1 + $df)); $idf = $idf * $idf; foreach ($matches as $match) { if ('user' == $match->type) { $match->doc = 'u_' . $match->item; } else { if (!in_array($match->type, array('post', 'attachment'))) { $match->doc = '**' . $match->type . '**' . $match->item; } } if (isset($match->taxonomy_detail)) { $match->taxonomy_score = 0; $match->taxonomy_detail = unserialize($match->taxonomy_detail); if (is_array($match->taxonomy_detail)) { foreach ($match->taxonomy_detail as $tax => $count) { if ($tax == 'post_tag') { $match->tag = $count; } if (empty($post_type_weights[$tax])) { $match->taxonomy_score += $count * 1; } else { $match->taxonomy_score += $count * $post_type_weights[$tax]; } } } } $match->tf = $match->title * $title_boost + $match->content + $match->comment * $comment_boost + $match->link * $link_boost + $match->author + $match->excerpt + $match->taxonomy_score + $match->customfield + $match->mysqlcolumn; $term_hits[$match->doc][$term] = $match->title + $match->content + $match->comment + $match->tag + $match->link + $match->author + $match->category + $match->excerpt + $match->taxonomy + $match->customfield + $match->mysqlcolumn; $match->weight = $match->tf * $idf; if ($recency_bonus) { $post = relevanssi_get_post($match->doc); if (strtotime($post->post_date) > $recency_cutoff_date) { $match->weight = $match->weight * $recency_bonus['bonus']; } } isset($body_matches[$match->doc]) ? $body_matches[$match->doc] += $match->content : ($body_matches[$match->doc] = $match->content); isset($title_matches[$match->doc]) ? $title_matches[$match->doc] += $match->title : ($title_matches[$match->doc] = $match->title); isset($link_matches[$match->doc]) ? $link_matches[$match->doc] += $match->link : ($link_matches[$match->doc] = $match->link); isset($tag_matches[$match->doc]) ? $tag_matches[$match->doc] += $match->tag : ($tag_matches[$match->doc] = $match->tag); isset($category_matches[$match->doc]) ? $category_matches[$match->doc] += $match->category : ($category_matches[$match->doc] = $match->category); isset($taxonomy_matches[$match->doc]) ? $taxonomy_matches[$match->doc] += $match->taxonomy : ($taxonomy_matches[$match->doc] = $match->taxonomy); isset($comment_matches[$match->doc]) ? $comment_matches[$match->doc] += $match->comment : ($comment_matches[$match->doc] = $match->comment); isset($relevanssi_post_types[$match->doc]) ? $type = $relevanssi_post_types[$match->doc] : ($type = null); if (!empty($post_type_weights[$type])) { $match->weight = $match->weight * $post_type_weights[$type]; } $match = apply_filters('relevanssi_match', $match, $idf); if ($match->weight == 0) { continue; } // the filters killed the match $post_ok = true; $post_ok = apply_filters('relevanssi_post_ok', $post_ok, $match->doc); if ($post_ok) { $doc_terms[$match->doc][$term] = true; // count how many terms are matched to a doc isset($doc_weight[$match->doc]) ? $doc_weight[$match->doc] += $match->weight : ($doc_weight[$match->doc] = $match->weight); isset($scores[$match->doc]) ? $scores[$match->doc] += $match->weight : ($scores[$match->doc] = $match->weight); if (is_numeric($match->doc)) { // this is to weed out taxonomies and users (t_XXX, u_XXX) $include_these_posts[$match->doc] = true; } } } } if (!isset($doc_weight)) { $no_matches = true; } if ($no_matches) { if ($search_again) { // no hits even with fuzzy search! $search_again = false; } else { if ("sometimes" == $fuzzy) { $search_again = true; $o_term_cond = "(term LIKE '%#term#' OR term LIKE '#term#%') "; } } } else { $search_again = false; } } while ($search_again); $strip_stops = true; $temp_terms_without_stops = array_keys(relevanssi_tokenize(implode(' ', $terms), $strip_stops)); $terms_without_stops = array(); foreach ($temp_terms_without_stops as $temp_term) { if (strlen($temp_term) >= $min_length) { array_push($terms_without_stops, $temp_term); } } $total_terms = count($terms_without_stops); if (isset($doc_weight)) { $doc_weight = apply_filters('relevanssi_results', $doc_weight); } if (isset($doc_weight) && count($doc_weight) > 0) { arsort($doc_weight); $i = 0; foreach ($doc_weight as $doc => $weight) { if (count($doc_terms[$doc]) < $total_terms && $operator == "AND") { // AND operator in action: // doc didn't match all terms, so it's discarded continue; } if (!empty($fields)) { if ($fields == 'ids') { $hits[intval($i)] = $doc; } if ($fields == 'id=>parent') { $object = new StdClass(); $object->ID = $doc; $object->post_parent = wp_get_post_parent_id($doc); $hits[intval($i)] = $object; } } else { $hits[intval($i)] = relevanssi_get_post($doc); $hits[intval($i)]->relevance_score = round($weight, 2); } $i++; } } if (count($hits) < 1) { if ($operator == "AND" and get_option('relevanssi_disable_or_fallback') != 'on') { $or_args = $args; $or_args['operator'] = "OR"; $or_args['q'] = relevanssi_add_synonyms($q); $return = relevanssi_search($or_args); extract($return); } } global $wp; $default_order = get_option('relevanssi_default_orderby', 'relevance'); if (empty($orderby)) { $orderby = $default_order; } // the sorting function checks for non-existing keys, cannot whitelist here if (empty($order)) { $order = 'desc'; } $order = strtolower($order); $order_accepted_values = array('asc', 'desc'); if (!in_array($order, $order_accepted_values)) { $order = 'desc'; } $orderby = apply_filters('relevanssi_orderby', $orderby); $order = apply_filters('relevanssi_order', $order); if ($orderby != 'relevance') { relevanssi_object_sort($hits, $orderby, $order); } $return = array('hits' => $hits, 'body_matches' => $body_matches, 'title_matches' => $title_matches, 'tag_matches' => $tag_matches, 'category_matches' => $category_matches, 'taxonomy_matches' => $taxonomy_matches, 'comment_matches' => $comment_matches, 'scores' => $scores, 'term_hits' => $term_hits, 'query' => $q, 'link_matches' => $link_matches); return $return; }
function relevanssi_search($q, $cat = NULL, $excat = NULL, $expost = NULL, $post_type = NULL, $taxonomy = NULL, $taxonomy_term = NULL, $operator = "AND", $tag = NULL, $author = NULL) { global $relevanssi_table, $wpdb; $values_to_filter = array('q' => $q, 'cat' => $cat, 'tag' => $tag, 'excat' => $excat, 'expost' => $expost, 'post_type' => $post_type, 'taxonomy' => $taxonomy, 'taxonomy_term' => $taxonomy_term, 'operator' => $operator, 'author' => $author); $filtered_values = apply_filters('relevanssi_search_filters', $values_to_filter); $q = $filtered_values['q']; $cat = $filtered_values['cat']; $excat = $filtered_values['excat']; $tag = $filtered_values['tag']; $expost = $filtered_values['expost']; $post_type = $filtered_values['post_type']; $taxonomy = $filtered_values['taxonomy']; $taxonomy_term = $filtered_values['taxonomy_term']; $operator = $filtered_values['operator']; $author = $filtered_values['author']; $hits = array(); $custom_cat = NULL; $o_cat = $cat; $o_excat = $excat; $o_expost = $expost; $o_tag = $tag; $o_post_type = $post_type; $o_taxonomy = $taxonomy; $o_taxonomy_term = $taxonomy_term; $o_author = $author; if ("custom" == $cat) { $custom_field = "custom"; $post_ids = array(); $results = $wpdb->get_results("SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key='{$custom_field}'"); foreach ($results as $row) { $post_ids[] = $row->post_id; } $custom_cat = implode(",", $post_ids); $cat = ""; } else { if ($cat) { $cats = explode(",", $cat); $inc_term_tax_ids = array(); $ex_term_tax_ids = array(); foreach ($cats as $t_cat) { $exclude = false; if ($t_cat < 0) { // Negative category, ie. exclusion $exclude = true; $t_cat = substr($t_cat, 1); // strip the - sign. } $t_cat = $wpdb->escape($t_cat); $term_tax_id = $wpdb->get_var("SELECT term_taxonomy_id FROM {$wpdb->term_taxonomy}\n\t\t\t\tWHERE term_id={$t_cat}"); if ($term_tax_id) { $exclude ? $ex_term_tax_ids[] = $term_tax_id : ($inc_term_tax_ids[] = $term_tax_id); $children = get_term_children($term_tax_id, 'category'); if (is_array($children)) { foreach ($children as $child) { $exclude ? $ex_term_tax_ids[] = $child : ($inc_term_tax_ids[] = $child); } } } } $cat = implode(",", $inc_term_tax_ids); $excat_temp = implode(",", $ex_term_tax_ids); } } if ($excat) { $excats = explode(",", $excat); $term_tax_ids = array(); foreach ($excats as $t_cat) { $t_cat = $wpdb->escape(trim($t_cat, ' -')); $term_tax_id = $wpdb->get_var("SELECT term_taxonomy_id FROM {$wpdb->term_taxonomy}\n\t\t\t\tWHERE term_id={$t_cat}"); if ($term_tax_id) { $term_tax_ids[] = $term_tax_id; } } $excat = implode(",", $term_tax_ids); } if (isset($excat_temp)) { $excat .= $excat_temp; } if ($author) { $author = esc_sql($author); } if ($tag) { $tags = explode(",", $tag); $inc_term_tax_ids = array(); $ex_term_tax_ids = array(); foreach ($tags as $t_tag) { $t_tag = $wpdb->escape($t_tag); $term_tax_id = $wpdb->get_var("SELECT term_taxonomy_id FROM {$wpdb->term_taxonomy}\n\t\t\t\tWHERE term_id={$t_tag}"); if ($term_tax_id) { $inc_term_tax_ids[] = $term_tax_id; } } $tag = implode(",", $inc_term_tax_ids); } if (!empty($taxonomy)) { $term_tax_id = null; $term_tax_id = $wpdb->get_var($wpdb->prepare("SELECT term_taxonomy_id FROM {$wpdb->terms}\n\t\t\tJOIN {$wpdb->term_taxonomy} USING(`term_id`)\n\t\t\t\tWHERE `slug` LIKE %s AND `taxonomy` LIKE %s", "%{$taxonomy_term}%", $taxonomy)); if ($term_tax_id) { $taxonomy = $term_tax_id; } else { $taxonomy = null; } } if (!$post_type && get_option('relevanssi_respect_exclude') == 'on') { if (function_exists('get_post_types')) { $pt_1 = get_post_types(array('exclude_from_search' => '0')); $pt_2 = get_post_types(array('exclude_from_search' => false)); $post_type = implode(',', array_merge($pt_1, $pt_2)); } } if ($post_type) { if (!is_array($post_type)) { $post_types = explode(',', $post_type); } else { $post_types = $post_type; } $pt_array = array(); foreach ($post_types as $pt) { $pt = "'" . trim(mysql_real_escape_string($pt)) . "'"; array_push($pt_array, $pt); } $post_type = implode(",", $pt_array); } //Added by OdditY: //Exclude Post_IDs (Pages) for non-admin search -> if ($expost) { if ($expost != "") { $aexpids = explode(",", $expost); foreach ($aexpids as $exid) { $exid = $wpdb->escape(trim($exid, ' -')); $postex .= " AND doc !='{$exid}'"; } } } // <- OdditY End $remove_stopwords = false; $phrases = relevanssi_recognize_phrases($q); $terms = relevanssi_tokenize($q, $remove_stopwords); if (count($terms) < 1) { // Tokenizer killed all the search terms. return $hits; } $terms = array_keys($terms); // don't care about tf in query $D = $wpdb->get_var("SELECT COUNT(DISTINCT(doc)) FROM {$relevanssi_table}"); $total_hits = 0; $title_matches = array(); $tag_matches = array(); $comment_matches = array(); $body_matches = array(); $scores = array(); $term_hits = array(); $fuzzy = get_option('relevanssi_fuzzy'); $query_restrictions = ""; if ($expost) { //added by OdditY $query_restrictions .= $postex; } if ($cat) { $query_restrictions .= " AND doc IN (SELECT DISTINCT(object_id) FROM {$wpdb->term_relationships}\n\t\t WHERE term_taxonomy_id IN ({$cat}))"; } if ($excat) { $query_restrictions .= " AND doc NOT IN (SELECT DISTINCT(object_id) FROM {$wpdb->term_relationships}\n\t\t WHERE term_taxonomy_id IN ({$excat}))"; } if ($post_type) { $query_restrictions .= " AND doc IN (SELECT DISTINCT(ID) FROM {$wpdb->posts}\n\t\t\tWHERE post_type IN ({$post_type}))"; } if ($tag) { $query_restrictions .= " AND doc IN (SELECT DISTINCT(object_id) FROM {$wpdb->term_relationships}\n\t\t WHERE term_taxonomy_id IN ({$tag}))"; } if ($phrases) { $query_restrictions .= " AND doc IN ({$phrases})"; } if ($custom_cat) { $query_restrictions .= " AND doc IN ({$custom_cat})"; } if ($taxonomy) { $query_restrictions .= " AND doc IN (SELECT DISTINCT(object_id) FROM {$wpdb->term_relationships}\n\t\t\tWHERE term_taxonomy_id IN ({$taxonomy}))"; } if ($author) { $query_restrictions .= " AND doc IN (SELECT DISTINCT(ID) FROM {$wpdb->posts}\n\t\t WHERE post_author IN ({$author}))"; } if (isset($_REQUEST['by_date'])) { $n = $_REQUEST['by_date']; $u = substr($n, -1, 1); switch ($u) { case 'h': $unit = "HOUR"; break; case 'd': $unit = "DAY"; break; case 'm': $unit = "MONTH"; break; case 'y': $unit = "YEAR"; break; case 'w': $unit = "WEEK"; break; default: $unit = "DAY"; } $n = preg_replace('/[hdmyw]/', '', $n); if (is_numeric($n)) { $query_restrictions .= " AND doc IN (SELECT DISTINCT(ID) FROM {$wpdb->posts}\n\t\t\t\tWHERE post_date > DATE_SUB(NOW(), INTERVAL {$n} {$unit}))"; } } $query_restrictions = apply_filters('relevanssi_where', $query_restrictions); // Charles St-Pierre $no_matches = true; if ("always" == $fuzzy) { $o_term_cond = "(term LIKE '%#term#' OR term LIKE '#term#%') "; } else { $o_term_cond = " term = '#term#' "; } $min_word_length = get_option('relevanssi_min_word_length', 3); $search_again = false; do { foreach ($terms as $term) { if (strlen($term) < $min_word_length) { continue; } $term = $wpdb->escape(like_escape($term)); $term_cond = str_replace('#term#', $term, $o_term_cond); $query = "SELECT doc, term, tf, title FROM {$relevanssi_table} WHERE {$term_cond} {$query_restrictions}"; $query = apply_filters('relevanssi_query_filter', $query); $matches = $wpdb->get_results($query); if (count($matches) < 1) { continue; } else { $no_matches = false; } relevanssi_populate_array($matches); $total_hits += count($matches); $query = "SELECT COUNT(DISTINCT(doc)) FROM {$relevanssi_table} WHERE {$term_cond} {$query_restrictions}"; $query = apply_filters('relevanssi_df_query_filter', $query); $df = $wpdb->get_var($query); if ($df < 1 && "sometimes" == $fuzzy) { $query = "SELECT COUNT(DISTINCT(doc)) FROM {$relevanssi_table}\n\t\t\t\t\tWHERE (term LIKE '%{$term}' OR term LIKE '{$term}%') {$query_restrictions}"; $query = apply_filters('relevanssi_df_query_filter', $query); $df = $wpdb->get_var($query); } $title_boost = floatval(get_option('relevanssi_title_boost')); $tag_boost = floatval(get_option('relevanssi_tag_boost')); $comment_boost = floatval(get_option('relevanssi_comment_boost')); $idf = log($D / (1 + $df)); foreach ($matches as $match) { $weight = $match->tf * $idf; if (!isset($term_hits[$match->doc][$term])) { $term_hits[$match->doc][$term] = 0; } switch ($match->title) { case "1": $weight = $weight * $title_boost; isset($title_matches[$match->doc]) ? $title_matches[$match->doc] += $match->tf : ($title_matches[$match->doc] = $match->tf); $term_hits[$match->doc][$term] += $match->tf; break; case "2": $weight = $weight * $tag_boost; isset($tag_matches[$match->doc]) ? $tag_matches[$match->doc] += $match->tf : ($tag_matches[$match->doc] = $match->tf); $term_hits[$match->doc][$term] += $match->tf; break; case "3": $weight = $weight * $comment_boost; isset($comment_matches[$match->doc]) ? $comment_matches[$match->doc] += $match->tf : ($comment_matches[$match->doc] = $match->tf); $term_hits[$match->doc][$term] += $match->tf; break; default: isset($body_matches[$match->doc]) ? $body_matches[$match->doc] += $match->tf : ($body_matches[$match->doc] = $match->tf); $term_hits[$match->doc][$term] += $match->tf; } $doc_terms[$match->doc][$term] = true; // count how many terms are matched to a doc isset($doc_weight[$match->doc]) ? $doc_weight[$match->doc] += $weight : ($doc_weight[$match->doc] = $weight); isset($scores[$match->doc]) ? $scores[$match->doc] += $weight : ($scores[$match->doc] = $weight); } } if ($no_matches) { if ($search_again) { // no hits even with fuzzy search! $search_again = false; } else { if ("sometimes" == $fuzzy) { $search_again = true; $o_term_cond = "(term LIKE '%#term#' OR term LIKE '#term#%') "; } } } else { $search_again = false; } } while ($search_again); $strip_stops = true; $temp_terms_without_stops = array_keys(relevanssi_tokenize(implode(' ', $terms), $strip_stops)); $terms_without_stops = array(); foreach ($temp_terms_without_stops as $temp_term) { if (strlen($temp_term) >= $min_word_length) { array_push($terms_without_stops, $temp_term); } } $total_terms = count($terms_without_stops); $doc_weight = apply_filters('relevanssi_results', $doc_weight); if (isset($doc_weight) && count($doc_weight) > 0) { arsort($doc_weight); $i = 0; foreach ($doc_weight as $doc => $weight) { if (count($doc_terms[$doc]) < $total_terms && $operator == "AND") { // AND operator in action: // doc didn't match all terms, so it's discarded continue; } $status = relevanssi_get_post_status($doc); $post_ok = true; $post_ok = apply_filters('relevanssi_post_ok', $doc); if ($post_ok) { $hits[intval($i++)] = relevanssi_get_post($doc); } } } if (count($hits) < 1) { if ($operator == "AND" and get_option('relevanssi_disable_or_fallback') != 'on') { $return = relevanssi_search($q, $o_cat, $o_excat, $o_expost, $o_post_type, $o_taxonomy, $o_taxonomy_term, "OR", $o_tag, $o_author); extract($return); } } global $wp; $default_order = get_option('relevanssi_default_orderby', 'relevance'); isset($wp->query_vars["orderby"]) ? $orderby = $wp->query_vars["orderby"] : ($orderby = $default_order); isset($wp->query_vars["order"]) ? $order = $wp->query_vars["order"] : ($order = 'desc'); if ($orderby != 'relevance') { objectSort($hits, $orderby, $order); } $return = array('hits' => $hits, 'body_matches' => $body_matches, 'title_matches' => $title_matches, 'tag_matches' => $tag_matches, 'comment_matches' => $comment_matches, 'scores' => $scores, 'term_hits' => $term_hits, 'query' => $q); return $return; }