Index: wp-includes/deprecated.php
===================================================================
--- wp-includes/deprecated.php	(revision 22254)
+++ wp-includes/deprecated.php	(working copy)
@@ -3328,4 +3328,16 @@
  * @since 2.3.0
  * @deprecated 3.5.0
  */
-function _save_post_hook() {}
\ No newline at end of file
+function _save_post_hook() {}
+
+/**
+ * Formerly used internally to tidy up the search terms.
+ *
+ * @access private
+ * @since 2.9.0
+ * @deprecated 3.5.0
+ */
+function _search_terms_tidy($t) {
+	_deprecated_function( __FUNCTION__, '3.5', '' );
+	return trim($t, "\"'\n\r ");
+}
Index: wp-includes/functions.php
===================================================================
--- wp-includes/functions.php	(revision 22254)
+++ wp-includes/functions.php	(working copy)
@@ -3470,19 +3470,6 @@
 }
 
 /**
- * Used internally to tidy up the search terms.
- *
- * @access private
- * @since 2.9.0
- *
- * @param string $t
- * @return string
- */
-function _search_terms_tidy($t) {
-	return trim($t, "\"'\n\r ");
-}
-
-/**
  * Returns true.
  *
  * Useful for returning true to filters easily.
@@ -3794,3 +3781,51 @@
 function wp_checkdate( $month, $day, $year, $source_date ) {
 	return apply_filters( 'wp_checkdate', checkdate( $month, $day, $year ), $source_date );
 }
+
+/**
+ * Check if the terms are suitable for searching.
+ *
+ * Includes array of stopwords (terms) that are excluded from the separate term matching when searching for posts.
+ * The list of English stopwords is the approximate search engines list. MySQL has a much longer default list of full-text stopwords.
+ *
+ * @access private
+ * @since 3.5.0
+ *
+ * @param array Terms to check
+ * @return array
+ */
+function _check_search_terms($terms) {
+	$strtolower_func = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower';
+	$checked = $stopwords = array();
+
+	$_words = explode( ',', _x('about,an,are,as,at,be,by,com,for,from,how,in,is,it,of,on,or,that,the,this,to,was,what,when,where,who,will,with,the,www', 'Comma separated list of common words to exclude when searching (stopwords).') );
+
+	foreach( $_words as $word ) {
+		$word = trim($word, "\r\n\t ");
+		if ( !$word )
+			continue;
+		$stopwords[] = $word;
+	}
+
+	$stopwords = apply_filters( 'wp_search_stopwords', $stopwords );
+
+	foreach ( $terms as $term ) {
+		// keep before/after spaces when term is for exact match
+		if ( preg_match('/^".+"$/', $term) )
+			$term = trim($term, "\"'");
+		else
+			$term = trim($term, "\"' ");
+
+		// \p{L} matches a single letter that is not a Chinese, Japanese, etc. char
+		if ( !$term || preg_match( '/^\p{L}$/u', $term ) )
+			continue;
+
+		if ( in_array( $strtolower_func($term), $stopwords, true ) )
+			continue;
+
+		$checked[] = $term;
+	}
+
+	return $checked;
+}
+
Index: wp-includes/query.php
===================================================================
--- wp-includes/query.php	(revision 22254)
+++ wp-includes/query.php	(working copy)
@@ -2180,22 +2180,40 @@
 			}
 		}
 
-		// If a search pattern is specified, load the posts that match
-		if ( !empty($q['s']) ) {
+		// If a search pattern is specified, load the posts that match.
+		// Sanity check: search string shouldn't be more than 1600 characters.
+		// See ticket #21688 for more info.
+		if ( !empty($q['s']) && strlen($q['s']) < 1600 ) {
 			// added slashes screw with quote grouping when done early, so done later
 			$q['s'] = stripslashes($q['s']);
 			if ( empty( $_GET['s'] ) && $this->is_main_query() )
 				$q['s'] = urldecode($q['s']);
+			// there are no line breaks in <input /> fields
+			$q['s'] = str_replace( array("\r", "\n"), '', $q['s'] );
+			$num_all_terms = 1;
 			if ( !empty($q['sentence']) ) {
 				$q['search_terms'] = array($q['s']);
 			} else {
-				preg_match_all('/".*?("|$)|((?<=[\r\n\t ",+])|^)[^\r\n\t ",+]+/', $q['s'], $matches);
-				$q['search_terms'] = array_map('_search_terms_tidy', $matches[0]);
+				if ( preg_match_all('/".*?("|$)|((?<=[\t ",+])|^)[^\t ",+]+/', $q['s'], $matches) ) {
+					$num_all_terms = count( $matches[0] );
+					$q['search_terms'] = _check_search_terms( $matches[0] );
+					// if the search string has only short terms or stopwords, or is 10+ terms long, match it as sentence
+					if ( empty($q['search_terms']) || count($q['search_terms']) > 9 )
+						$q['search_terms'] = array($q['s']);
+				} else {
+					$q['search_terms'] = array($q['s']);
+				}
 			}
+
 			$n = !empty($q['exact']) ? '' : '%';
 			$searchand = '';
-			foreach( (array) $q['search_terms'] as $term ) {
+			$search_orderby_title = array();
+			foreach ( $q['search_terms'] as $term ) {
 				$term = esc_sql( like_escape( $term ) );
+
+				if ( $n )
+					$search_orderby_title[] = "$wpdb->posts.post_title LIKE '%$term%'";
+
 				$search .= "{$searchand}(($wpdb->posts.post_title LIKE '{$n}{$term}{$n}') OR ($wpdb->posts.post_content LIKE '{$n}{$term}{$n}'))";
 				$searchand = ' AND ';
 			}
@@ -2395,6 +2413,53 @@
 				$orderby .= " {$q['order']}";
 		}
 
+		// Order search results by relevance when another "orderby" is not specified in the query
+		if ( !empty($search_orderby_title) && empty($q['orderby']) ) {
+			$search_orderby = '';
+			// Allow plugins to override sorting on 'post_title' and/or 'post_content'.
+			// Passing an empty array would disable sorting.
+			$search_orderby_on = array('post_title', 'post_content');
+			$search_orderby_on = apply_filters_ref_array( 'posts_search_orderby_on', array( $search_orderby_on, &$this ) );
+
+			if ( $num_all_terms > 1 ) {
+				$num_terms = count($search_orderby_title);
+				$search_orderby_s = esc_sql( like_escape($q['s']) );
+
+				if ( in_array('post_title', $search_orderby_on, true) ) {
+					$search_orderby = '(CASE ';
+					// sentence match in 'post_title'
+					$search_orderby .= "WHEN $wpdb->posts.post_title LIKE '%{$search_orderby_s}%' THEN 1 ";
+
+					// sanity limit, sort as sentence when more than 6 terms
+					// (few searches are longer than 6 terms and most titles are not)
+					if ( $num_terms < 7 ) {
+						// all words in title
+						$search_orderby .= 'WHEN ' . implode( ' AND ', $search_orderby_title ) . ' THEN 2 ';
+						// any word in title, not needed when $num_terms == 1
+						if ( $num_terms > 1 )
+							$search_orderby .= 'WHEN ' . implode( ' OR ', $search_orderby_title ) . ' THEN 3 ';
+					}
+
+					// sentence match in 'post_content'
+					if ( in_array('post_content', $search_orderby_on, true) )
+						$search_orderby .= "WHEN $wpdb->posts.post_content LIKE '%{$search_orderby_s}%' THEN 4 ";
+
+					$search_orderby .= 'ELSE 5 END)';
+				} elseif ( in_array('post_content', $search_orderby_on, true) ) {
+					// Not sorting on 'post_title', order by sentence matches in 'post_content'
+					$search_orderby = "$wpdb->posts.post_content LIKE '%{$search_orderby_s}%' DESC";
+				}
+			} else {
+				// single word or sentence search
+				if ( in_array('post_title', $search_orderby_on, true) )
+					$search_orderby = reset($search_orderby_title) . ' DESC';
+			}
+			// Allow plugins to add/remove/modify the 'order by' for the search section of the database query
+			$search_orderby = apply_filters_ref_array( 'posts_search_orderby', array( $search_orderby, &$this ) );
+			if ( $search_orderby )
+				$orderby = $orderby ? $search_orderby . ', ' . $orderby : $search_orderby;
+		}
+
 		if ( is_array( $post_type ) ) {
 			$post_type_cap = 'multiple_post_type';
 		} else {
