get_results( "SELECT * FROM $wpdb->postmeta WHERE ( meta_key = '_relevanssi_pin' OR meta_key = '_relevanssi_unpin' OR meta_key = '_relevanssi_pin_for_all' ) AND meta_value != '' LIMIT 1" ); if ( ! is_multisite() && empty( $results ) ) { // No, nothing is pinned. return $hits; } // Disable all filter functions on 'relevanssi_stemmer'. if ( isset( $wp_filter['relevanssi_stemmer'] ) ) { $callbacks = $wp_filter['relevanssi_stemmer']->callbacks; $wp_filter['relevanssi_stemmer']->callbacks = null; } $terms = relevanssi_tokenize( $hits[1], false, -1, 'search_query' ); // Re-enable the removed filters. if ( isset( $wp_filter['relevanssi_stemmer'] ) ) { $wp_filter['relevanssi_stemmer']->callbacks = $callbacks; } $escaped_terms = array(); foreach ( array_keys( $terms ) as $term ) { $escaped_terms[] = esc_sql( trim( $term ) ); } $term_list = array(); $count_escaped_terms = count( $escaped_terms ); for ( $length = 1; $length <= $count_escaped_terms; $length++ ) { for ( $offset = 0; $offset <= $count_escaped_terms - $length; $offset++ ) { $slice = array_slice( $escaped_terms, $offset, $length ); $term_list[] = implode( ' ', $slice ); } } $full_search_phrase = esc_sql( trim( $hits[1] ) ); if ( ! in_array( $full_search_phrase, $term_list, true ) ) { $term_list[] = $full_search_phrase; } /** * Doing this instead of individual get_post_meta() calls can cut hundreds * of database queries! */ $posts_pinned_for_all = array_flip( $wpdb->get_col( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_relevanssi_pin_for_all' AND meta_value = 'on'" ) ); $pin_weights_sql = $wpdb->get_results( "SELECT post_id, meta_value FROM $wpdb->postmeta WHERE meta_key = '_relevanssi_pin_weights'" ); $pin_weights = array(); foreach ( $pin_weights_sql as $row ) { $pin_weights[ $row->post_id ] = $row->meta_value; } unset( $pin_weights_sql ); /** * If the search query is "foo bar baz", $term_list now contains "foo", "bar", *"baz", "foo bar", "bar baz", and "foo bar baz". */ if ( is_array( $term_list ) ) { $term_list_array = $term_list; array_multisort( array_map( 'relevanssi_strlen', $term_list_array ), SORT_DESC, $term_list_array ); $term_list = implode( "','", $term_list ); $term_list = "'$term_list'"; $positive_ids = array(); $negative_ids = array(); $pins_fetched = false; $pinned_posts = array(); $other_posts = array(); foreach ( $hits[0] as $hit ) { $object_array = relevanssi_get_an_object( $hit ); $hit = $object_array['object']; $return_value = $object_array['format']; $blog_id = 0; if ( isset( $hit->blog_id ) && function_exists( 'switch_to_blog' ) ) { // Multisite, so switch_to_blog() to correct blog and process // the pinned hits per blog. $blog_id = $hit->blog_id; switch_to_blog( $blog_id ); if ( ! isset( $pins_fetched[ $blog_id ] ) ) { $positive_ids[ $blog_id ] = $wpdb->get_col( 'SELECT post_id FROM ' . $wpdb->prefix . "postmeta WHERE meta_key = '_relevanssi_pin' AND meta_value IN ( $term_list )" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared $negative_ids[ $blog_id ] = $wpdb->get_col( 'SELECT post_id FROM ' . $wpdb->prefix . "postmeta WHERE meta_key = '_relevanssi_unpin' AND meta_value IN ( $term_list )" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared if ( ! is_array( $pins_fetched ) ) { $pins_fetched = array(); } $pins_fetched[ $blog_id ] = true; } restore_current_blog(); } elseif ( ! $pins_fetched ) { // Single site. $positive_ids[0] = $wpdb->get_col( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_relevanssi_pin' AND meta_value IN ( $term_list )" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared $negative_ids[0] = $wpdb->get_col( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_relevanssi_unpin' AND meta_value IN ( $term_list )" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared $pins_fetched = true; } $hit_id = strval( $hit->ID ); // The IDs from the database are strings, the one from the post is an integer in some contexts. $positive_match = isset( $positive_ids[ $blog_id ] ) && is_array( $positive_ids[ $blog_id ] ) && in_array( $hit_id, $positive_ids[ $blog_id ], true ); $negative_match = isset( $negative_ids[ $blog_id ] ) && is_array( $negative_ids[ $blog_id ] ) && in_array( $hit_id, $negative_ids[ $blog_id ], true ); $pinned_for_all = isset( $hit->ID ) && isset( $posts_pinned_for_all[ $hit->ID ] ); $pin_weight = 0; $weights = unserialize( $pin_weights[ $hit->ID ] ?? '' ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize foreach ( $term_list_array as $term ) { if ( isset( $weights[ $term ] ) ) { $pin_weight = $weights[ $term ]; break; } } if ( 0 === $pin_weight ) { $term = $term_list_array[0]; $pin_weight = 1; } if ( $hit_id && $positive_match && ! $negative_match ) { $hit->relevanssi_pinned = 1; $pinned_posts[ $term ][ $pin_weight ][] = relevanssi_return_value( $hit, $return_value ); } elseif ( $pinned_for_all && ! $negative_match ) { $hit->relevanssi_pinned = 1; $pinned_posts[0][0][] = relevanssi_return_value( $hit, $return_value ); } elseif ( ! $negative_match ) { $other_posts[] = relevanssi_return_value( $hit, $return_value ); } } array_multisort( array_map( 'relevanssi_strlen', array_keys( $pinned_posts ) ), SORT_DESC, $pinned_posts ); $all_pinned_posts = array(); foreach ( $pinned_posts as $term => $posts_for_term ) { krsort( $posts_for_term, SORT_NUMERIC ); $posts_for_term = call_user_func_array( 'array_merge', $posts_for_term ); $all_pinned_posts = array_merge( $all_pinned_posts, $posts_for_term ); } $hits[0] = array_merge( $all_pinned_posts, $other_posts ); } return $hits; } /** * Adds pinned words to post content. * * Adds pinned terms to post content to make sure posts are found with the * pinned terms. * * @param string $content Post content. * @param object $post The post object. */ function relevanssi_add_pinned_words_to_post_content( $content, $post ) { $pin_words = get_post_meta( $post->ID, '_relevanssi_pin', false ); foreach ( $pin_words as $word ) { $content .= " $word"; } return $content; } /** * Adds pinned words to post title. * * If the `relevanssi_index_content` filter hook returns `false`, ie. post * content is not indexed, this function will add the pinned words to the post * title instead to guarantee they are found in the search. * * @param string $content Titlecontent. * @param object $post The post object. */ function relevanssi_pinning_backup( $content, $post ) { if ( false === apply_filters( 'relevanssi_index_content', true ) ) { $content = relevanssi_add_pinned_words_to_post_content( $content, $post ); } return $content; } /** * Provides the pinning functionality for the admin search. * * @param object $post The post object. * @param string $query The search query. * * @return array First item is a string containing the pinning buttons, the second * item is a string containing the "pinned" notice if the post is * pinned. */ function relevanssi_admin_search_pinning( $post, $query ) { $pinned = ''; $pinning_buttons = array(); $pinned_words = array(); if ( isset( $post->relevanssi_pinned ) ) { $pinned_words = get_post_meta( $post->ID, '_relevanssi_pin' ); $pinned = '' . __( '(pinned)', 'relevanssi' ) . ''; } if ( ! current_user_can( 'edit_post', $post->ID ) ) { return array( '', $pinned ); } $tokens = relevanssi_tokenize( $query, true, -1, 'search_query' ); foreach ( array_keys( $tokens ) as $token ) { if ( ! in_array( $token, $pinned_words, true ) ) { /* Translators: %s is the search term. */ $pinning_button = sprintf( '', $post->ID, $token, sprintf( __( "Pin for '%s'", 'relevanssi' ), $token ) ); $pinning_buttons[] = $pinning_button; } else { /* Translators: %s is the search term. */ $pinning_button = sprintf( '', $post->ID, $token, sprintf( __( "Unpin for '%s'", 'relevanssi' ), $token ) ); $pinning_buttons[] = $pinning_button; } } $pinning_buttons = implode( ' ', $pinning_buttons ); return array( $pinning_buttons, $pinned ); }