MySQL 8 Full-Text Search Guide

13 min read 2,403 words
MySQL 8 Full-Text Search: Building a Search Engine Without Elasticsearch featured image

Understanding MySQL 8 Full-Text Search Capabilities

Full-text search lets users find records by searching for words and phrases within text fields, rather than relying on exact matches. This approach matters when building features like site search, article discovery, knowledge base navigation, or any interface where users need to locate content using natural language queries.

MySQL 8 introduced significant improvements to full-text indexing capabilities on InnoDB tables, making it a viable option for many search requirements that previously would have required dedicated search infrastructure. The implementation handles practical search requirements without requiring additional services like Elasticsearch or Algolia, which adds operational complexity to your stack.

Before deciding which search approach fits your project, understanding what MySQL 8 can do natively helps you make informed choices about where to invest development effort. Many projects that immediately reach for external search engines could serve their users well with MySQL's built-in capabilities.

Creating a Full-Text Index in MySQL 8

Full-text indexes in MySQL work on CHAR, VARCHAR, and TEXT columns. You can add one to an existing table using ALTER TABLE, or include the index definition directly in your CREATE TABLE statement.

ALTER TABLE blog_posts
ADD FULLTEXT INDEX ft_title_content (title, content);
CREATE TABLE blog_posts (
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(255) NOT NULL,
    content MEDIUMTEXT NOT NULL,
    author VARCHAR(100),
    published_at DATETIME,
    FULLTEXT INDEX ft_search (title, content) WITH PARSER ngram
) ENGINE=InnoDB;

The ngram parser is essential when working with languages that use character-by-character word boundaries, such as Chinese, Japanese, or Korean. For English and most Western languages, MySQL's default parser handles word boundaries correctly without additional configuration.

When designing your table structure, consider that longer VARCHAR columns or TEXT fields give users more content to search through, but also affect index size and query performance. Choosing the right column length for your use case matters for long-term maintainability, particularly on tables that will grow significantly over time.

Natural Language Mode vs Boolean Mode

MySQL provides two primary modes for querying full-text indexes. Each serves different use cases and offers different control over search behaviour, so understanding both helps you choose the right approach for your application.

Natural Language Mode

Natural Language mode is the default in MySQL 8. The database interprets the search string as a list of words and returns results ranked by relevance. The relevance score reflects how well each record matches the search terms based on factors like term frequency and inverse document frequency. This mode works well when you want straightforward keyword matching without complex operator handling.

SELECT id, title, MATCH(title, content) AGAINST('ubuntu server security' IN NATURAL LANGUAGE MODE) AS relevance
FROM blog_posts
WHERE MATCH(title, content) AGAINST('ubuntu server security' IN NATURAL LANGUAGE MODE)
ORDER BY relevance DESC
LIMIT 10;

Boolean Mode

Boolean mode gives you explicit control over how each term affects the search. You can require certain words, exclude others, and use wildcards for prefix matching. This mode does not return relevance scores by default, though you can still calculate them if needed for custom sorting logic.

SELECT id, title, excerpt
FROM blog_posts
WHERE MATCH(title, content) AGAINST('+ubuntu +security -apache' IN BOOLEAN MODE);

Boolean Mode Operators

The Boolean mode operators available in MySQL full-text searches give you fine-grained control over query behaviour. These operators let you build sophisticated search queries that go beyond simple keyword matching.

  • +word — the search result must contain this word
  • -word — the search result must not contain this word
  • word* — matches any word starting with this prefix
  • "exact phrase" — matches the exact phrase within quotation marks
  • +word1 +word2 — both words must be present in results
  • word1 word2 — either word may be present in results

Boolean mode is generally safer for handling user input because it ignores special operators unless they are explicitly included in the query string. This reduces the risk of syntax errors when users type characters that MySQL would otherwise interpret as operators.

How MySQL Calculates Relevance Scores

MySQL's relevance scoring depends on several factors working together. The engine evaluates term frequency (how often a word appears in a document), inverse document frequency (how rare or common the word is across all records), and field length (shorter fields with matching terms score higher than longer ones).

By default, MySQL weights matches in title fields higher than matches in body content. This makes sense for most use cases where titles are more descriptive of content than body text. However, you can adjust this manually by creating weighted scores in your query to match your specific ranking requirements.

SELECT id, title,
    (MATCH(title, content) AGAINST(:query IN BOOLEAN MODE) * 3 +
     MATCH(content) AGAINST(:query2 IN BOOLEAN MODE)) AS weighted_relevance
FROM blog_posts
WHERE MATCH(title, content) AGAINST(:query3 IN BOOLEAN MODE)
ORDER BY weighted_relevance DESC;

This example gives title matches three times the weight of content matches. Adjust the multiplier based on what matters most for your specific application. For some projects, a 2:1 ratio works better; for others, you might want equal weighting or something higher depending on how users typically search.

Beyond MySQL's built-in scoring, you can post-process results in your application layer to incorporate additional ranking signals. Recency, category relevance, view counts, user ratings, or explicit popularity metrics can all factor into how you sort final results. Combining MySQL's relevance scores with application-level customisation often produces better user experiences than relying on defaults alone.

Adding Search to a PHP Application

Integrating MySQL full-text search into a PHP application involves building queries safely in your data access layer. Using prepared statements protects against SQL injection while handling user input correctly. Modern PHP with its type declarations and strict typing makes building robust search functions straightforward.

function searchPosts(PDO $pdo, string $query, int $limit = 10): array
{
    $query = trim($query);
    if (strlen($query) < 2) {
        return [];
    }

    // Escape special characters for boolean mode
    $escaped = preg_replace('/[+\-><()~*\"@]/', '', $query);
    $searchTerm = '"' . addslashes($escaped) . '"';

    $sql = "SELECT id, title, content,
            MATCH(title, content) AGAINST(:query IN BOOLEAN MODE) AS relevance
            FROM blog_posts
            WHERE MATCH(title, content) AGAINST(:query2 IN BOOLEAN MODE)
            ORDER BY relevance DESC
            LIMIT :limit";

    $stmt = $pdo->prepare($sql);
    $stmt->bindValue(':query', $searchTerm, PDO::PARAM_STR);
    $stmt->bindValue(':query2', $searchTerm, PDO::PARAM_STR);
    $stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
    $stmt->execute();

    return $stmt->fetchAll(PDO::FETCH_ASSOC);
}

This function strips Boolean operators from raw user input before constructing the search term, then wraps it in quotes for phrase matching. The prepared statement ensures proper escaping of any remaining special characters. For shorter queries, the function returns an empty array rather than executing a potentially expensive search on a single character.

PHP 8 introduced several features that improve how you write database interaction code, including named parameters and union types that make search functions more maintainable and type-safe.

Optimising Performance for Large Datasets

Full-text indexes can slow down on tables containing millions of rows. Several strategies help maintain responsive search performance as your data grows. Planning for scale early prevents performance issues from becoming user experience problems later.

Table Partitioning by Date

Partitioning works well when most searches focus on recent content. By dividing the table into partitions by month or year, queries can skip older partitions that are unlikely to contain relevant results. This approach significantly reduces the data MySQL must scan for time-sensitive searches.

CREATE TABLE blog_posts (
    id INT,
    title VARCHAR(255),
    content MEDIUMTEXT,
    published_at DATE,
    FULLTEXT INDEX ft_search (title, content)
) PARTITION BY RANGE (YEAR(published_at)) (
    PARTITION p2023 VALUES LESS THAN (2024),
    PARTITION p2024 VALUES LESS THAN (2025),
    PARTITION p2025 VALUES LESS THAN (2026),
    PARTITION pmax VALUES LESS THAN MAXVALUE
);

Query Caching

Caching reduces database load for frequently searched terms on relatively static content. Caching search results for a short period prevents repeated identical queries from hitting the database, which is particularly valuable when your application serves many users searching for popular topics.

function searchWithCache(PDO $pdo, string $query, int $ttl = 300): array
{
    $cacheKey = 'search:' . md5($query);
    $cached = apcu_fetch($cacheKey);

    if ($cached !== false) {
        return $cached;
    }

    $results = searchPosts($pdo, $query);
    apcu_store($cacheKey, $results, $ttl);

    return $results;
}

The cache TTL of 300 seconds (five minutes) balances freshness against database load. Adjust this based on how frequently your content changes. A site with daily updates might use a shorter TTL, while a documentation site with rare changes could cache for longer periods.

Index Maintenance

Beyond partitioning and caching, regular database maintenance matters. Ensuring your MySQL configuration matches your workload, keeping statistics updated, and monitoring query performance over time all contribute to consistent search responsiveness. A solid database indexing strategy supports full-text search performance alongside your other query patterns.

When MySQL Full-Text Search Reaches Its Limits

MySQL full-text search handles most simple to moderate search requirements effectively. However, certain features require capabilities that MySQL does not natively provide. Recognising when you need additional tools helps you make practical decisions about your search architecture.

  • Fuzzy matching and typo tolerance: MySQL does not find "servert" when searching for "server" without additional extensions or external tools. Users who misspell words typically see no results, which frustrates users and reduces engagement with your search feature.
  • Faceted search: Counting matches per category or tag efficiently requires multiple queries or complex subqueries, which do not scale as well as faceted search in dedicated search engines. Building faceted search with MySQL alone is possible but requires careful optimisation.
  • Relevance tuning based on user behaviour: You can manually weight fields, but dynamically adjusting relevance based on click-through rates or user interactions requires building additional infrastructure to track and incorporate user behaviour signals.
  • Autocomplete and type-ahead: MySQL full-text does not natively support prefix completion as you type. Prefix wildcards work but do not provide the ranked suggestions that type-ahead interfaces typically need for good user experiences.

For applications that require fuzzy matching, sophisticated relevance tuning, or advanced search features, dedicated search engines like Elasticsearch provide significantly more capability. A direct comparison of MySQL full-text search against Elasticsearch can help you evaluate which approach suits your specific requirements.

The right approach depends on your specific situation. A small business website with basic site search can often run entirely on MySQL full-text search. A content-heavy platform with millions of records and advanced search requirements may benefit from dedicated search infrastructure, but this comes with added hosting complexity and operational overhead.

Related practical reading

These related guides can help you connect this topic with the wider website, server, security, and support decisions around it.

Putting It Together

MySQL 8 full-text search provides a capable foundation for building search functionality without adding external services to your stack. The combination of Natural Language and Boolean search modes, configurable relevance scoring, and reasonable performance characteristics handles a wide range of practical use cases that many applications need.

The key is matching the tool to the requirement. Basic site search, article discovery, and keyword-based filtering all work well with MySQL's native capabilities. When your users expect typo tolerance, sophisticated relevance tuning, or advanced faceting, you will likely need to look beyond what MySQL provides and evaluate dedicated search infrastructure.

If you are building or maintaining a PHP application with search requirements and want to evaluate whether MySQL full-text search suits your needs, reviewing your current database setup alongside your search implementation often reveals practical improvements for both performance and relevance. Taking time to understand how MySQL handles your specific data and query patterns helps you make informed decisions about where to invest development effort.

If you need help reviewing your current search implementation or want a practical assessment of whether MySQL full-text search fits your project requirements, you can get in touch with details of your setup and search requirements.

Frequently Asked Questions

Can I use MySQL full-text search with multiple tables?
Yes. You can create full-text indexes on multiple tables and query them separately, or use a UNION approach to search across tables. For complex multi-table search requirements, consider whether a single indexed table or a search-specific table containing denormalised data works better for your use case.
Does InnoDB full-text search work with replication?
Full-text indexes are replicated in MySQL replication setups. However, note that ALTER TABLE operations that rebuild full-text indexes can cause replication lag on busy primary servers. Schedule index changes during lower-traffic periods where possible to minimise impact on your application's availability.
How does MySQL full-text search handle stopwords?
MySQL maintains a default stopword list containing common words like "the", "is", and "at" that are excluded from full-text indexes. You can customise this list using the ft_stopword_file system variable. Words on the stopword list will not match in searches, which may or may not be desirable depending on your content and user expectations.
What minimum word length applies to full-text searches?
MySQL has configurable minimum and maximum word length settings (ft_min_word_len and ft_max_word_len) that determine which words get indexed. By default, words shorter than three characters are typically excluded. This affects searches for abbreviations, product codes, or short terms. Changing these values requires rebuilding the full-text index using OPTIMIZE TABLE.
How do I rebuild a full-text index after changing configuration?
After modifying full-text configuration options like stopword lists, minimum word length, or parser settings, rebuild the index using OPTIMIZE TABLE or REPAIR TABLE. This applies the configuration changes to existing data and ensures new searches respect your updated settings.
Can I combine full-text search with regular WHERE clause conditions?
Yes. Full-text MATCH() ... AGAINST() conditions work alongside regular WHERE clause filters. MySQL can use the full-text index for the text search and then filter results with additional conditions. However, combining very restrictive full-text searches with highly selective WHERE conditions may not always use both indexes efficiently, depending on query planning.
Is MySQL full-text search suitable for e-commerce product search?
For small catalogues with straightforward product search needs, MySQL full-text search can work adequately. However, e-commerce search typically benefits from fuzzy matching, synonym handling, attribute-based faceting, and ranking based on sales data or user behaviour. These capabilities are areas where dedicated search platforms generally outperform MySQL's native features.