MySQL Full-Text Indexes
Introduction
When working with databases that contain large amounts of text data, standard comparison operators like LIKE
or regular expressions can become inefficient. MySQL's full-text indexing provides a powerful solution for performing fast text searches across your text columns. Full-text indexes enable natural language search capabilities, allowing you to find records based on relevance rankings rather than exact matches.
In this tutorial, you'll learn:
- What full-text indexes are and how they differ from regular indexes
- How to create and configure full-text indexes
- Different search modes available with full-text search
- Practical examples demonstrating full-text search functionality
- Best practices and performance considerations
What Are Full-Text Indexes?
Full-text indexes are specialized indexes designed specifically for text search operations. Unlike regular B-tree indexes that excel at finding exact matches or ranges, full-text indexes are optimized for searching through large amounts of textual content.
Key Features of Full-Text Indexes:
- Word-based indexing: Indexes individual words rather than the entire column
- Relevance ranking: Returns results ordered by relevance to the search query
- Natural language processing: Understands word variations and can ignore common words
- Boolean mode: Supports complex queries with operators like AND, OR, and NOT
- Phrase searching: Can match exact phrases in addition to individual words
When to Use Full-Text Indexes:
Full-text indexes are ideal when:
- You need to search through large text fields (like articles, descriptions, or comments)
- You want to rank results by relevance
- Your queries include natural language patterns
- Standard
LIKE
queries are becoming performance bottlenecks
Creating Full-Text Indexes
You can create full-text indexes at table creation time or add them to existing tables.
Creating a Full-Text Index on a New Table
CREATE TABLE articles (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(200),
content TEXT,
author VARCHAR(100),
published_date DATE,
FULLTEXT INDEX ft_content (content),
FULLTEXT INDEX ft_title_content (title, content)
);
In this example, we created:
- A single-column full-text index on the
content
column - A multi-column full-text index on both
title
andcontent
columns
Adding a Full-Text Index to an Existing Table
ALTER TABLE articles
ADD FULLTEXT INDEX ft_content (content);
Or using the CREATE INDEX syntax:
CREATE FULLTEXT INDEX ft_title_content ON articles (title, content);
Full-Text Index Storage Engines
Full-text indexes are supported by:
- InnoDB (from MySQL 5.6 and later)
- MyISAM
For optimal full-text search performance, ensure your MySQL version supports full-text indexing for your chosen storage engine.
Using Full-Text Search
MySQL provides three different modes for full-text searching:
- Natural Language Mode - The default mode that searches for words and ranks results
- Boolean Mode - Allows special operators for more complex searches
- Query Expansion - Enhances searches based on the most relevant results from an initial search
Natural Language Mode
This is the default mode for full-text searches.
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('database optimization');
This query will:
- Search for "database" and "optimization" in the title and content columns
- Order results by relevance score (highest first)
- Filter out rows with low relevance scores
You can also get the relevance score:
SELECT *, MATCH(title, content) AGAINST('database optimization') AS score
FROM articles
WHERE MATCH(title, content) AGAINST('database optimization')
ORDER BY score DESC;
Example Output:
+----+-------------------------+-------------------------------------------+----------+---------------+--------+
| id | title | content | author | published_date| score |
+----+-------------------------+-------------------------------------------+----------+---------------+--------+
| 5 | MySQL Database Optimization| Tips for optimizing your MySQL database ...| John Doe | 2023-01-15 | 2.7549 |
| 12 | Optimizing SQL Queries | Database optimization begins with proper...| Jane Smith| 2023-03-22 | 1.3856 |
| 8 | Database Design Patterns| When designing a database, optimization...| Alex Wong| 2022-11-10 | 0.9235 |
+----+-------------------------+-------------------------------------------+----------+---------------+--------+
Boolean Mode
Boolean mode gives you more control over your search with special operators.
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('+database -cloud +optimization' IN BOOLEAN MODE);
This search finds articles:
- That MUST contain "database" (
+database
) - That MUST NOT contain "cloud" (
-cloud
) - That MUST contain "optimization" (
+optimization
)
Boolean Mode Operators
Operator | Description | Example |
---|---|---|
+ | The word must be present | +mysql |
- | The word must not be present | -oracle |
* | Wildcard (suffix only) | data* matches database, datatype |
" | Phrase search | "database server" |
() | Grouping | (mysql postgresql) +performance |
> | Increases word contribution to relevance | >mysql optimization |
< | Decreases word contribution to relevance | <theory optimization |
Boolean Mode Example:
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('"mysql performance" +optimization -theory' IN BOOLEAN MODE);
This finds articles containing the exact phrase "mysql performance" and the word "optimization" but excluding articles containing "theory".
Query Expansion (With Expansion)
Query expansion performs an initial search and then does a second pass using the most relevant terms from the first search.
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('mysql index' WITH QUERY EXPANSION);
This is useful when:
- Users provide short queries
- You want to find related content beyond exact keyword matches
- You need more comprehensive results for ambiguous searches
Practical Example: Building a Simple Search Engine
Let's implement a simple article search feature using full-text indexes.
1. First, create and populate a table:
-- Create articles table with full-text indexes
CREATE TABLE articles (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(200) NOT NULL,
content TEXT NOT NULL,
category VARCHAR(100),
published_date DATE,
FULLTEXT INDEX ft_title_content (title, content)
);
-- Insert sample data
INSERT INTO articles (title, content, category, published_date)
VALUES
('MySQL Performance Tuning', 'Learn how to optimize MySQL database performance through proper indexing and query optimization techniques.', 'Database', '2023-01-15'),
('Getting Started with NoSQL', 'NoSQL databases like MongoDB provide scalable alternatives to traditional relational databases.', 'Database', '2023-02-20'),
('SQL Query Optimization', 'Optimize your SQL queries by understanding execution plans and index usage in database systems.', 'Database', '2023-03-10'),
('Web Development with Node.js', 'Build scalable web applications using Node.js and modern JavaScript frameworks.', 'Web Development', '2023-04-05'),
('Database Indexing Strategies', 'Proper indexing is crucial for database performance. Learn various indexing techniques including B-tree and hash indexes.', 'Database', '2023-05-12');
2. Create a PHP function for article search:
<?php
function searchArticles($searchTerm, $mode = 'NATURAL') {
$conn = new mysqli('localhost', 'username', 'password', 'database');
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
// Escape search term
$searchTerm = $conn->real_escape_string($searchTerm);
// Define search mode
$searchMode = $mode === 'BOOLEAN' ? 'IN BOOLEAN MODE' : '';
// Perform search
$sql = "SELECT *, MATCH(title, content) AGAINST('$searchTerm' $searchMode) AS relevance
FROM articles
WHERE MATCH(title, content) AGAINST('$searchTerm' $searchMode)
ORDER BY relevance DESC";
$result = $conn->query($sql);
$articles = [];
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
$articles[] = $row;
}
}
$conn->close();
return $articles;
}
// Example usage:
$results = searchArticles('database performance');
$booleanResults = searchArticles('+database -nosql', 'BOOLEAN');
?>
3. Create a simple search interface:
<!DOCTYPE html>
<html>
<head>
<title>Article Search</title>
<style>
.result { margin-bottom: 20px; padding: 10px; border: 1px solid #ddd; }
.relevance { color: #888; font-size: 0.8em; }
</style>
</head>
<body>
<h1>Article Search</h1>
<form method="GET">
<input type="text" name="q" placeholder="Search articles..." value="<?php echo htmlspecialchars($_GET['q'] ?? ''); ?>">
<select name="mode">
<option value="NATURAL" <?php echo ($_GET['mode'] ?? '') === 'NATURAL' ? 'selected' : ''; ?>>Natural Language</option>
<option value="BOOLEAN" <?php echo ($_GET['mode'] ?? '') === 'BOOLEAN' ? 'selected' : ''; ?>>Boolean Mode</option>
</select>
<button type="submit">Search</button>
</form>
<div id="results">
<?php
if (isset($_GET['q']) && !empty($_GET['q'])) {
$results = searchArticles($_GET['q'], $_GET['mode'] ?? 'NATURAL');
if (empty($results)) {
echo "<p>No results found.</p>";
} else {
foreach ($results as $article) {
echo "<div class='result'>";
echo "<h3>" . htmlspecialchars($article['title']) . "</h3>";
echo "<p>" . htmlspecialchars($article['content']) . "</p>";
echo "<div class='relevance'>Relevance score: " . number_format($article['relevance'], 4) . "</div>";
echo "<div>Category: " . htmlspecialchars($article['category']) . " | Published: " . $article['published_date'] . "</div>";
echo "</div>";
}
}
}
?>
</div>
</body>
</html>
Performance Considerations
To get the most out of full-text indexes:
-
Index size: Full-text indexes can be large. Only include columns that need to be searched.
-
Stopwords: MySQL ignores common words (like "the", "and", "is") by default. You can customize the stopword list:
-- View current stopword list
SELECT * FROM INFORMATION_SCHEMA.INNODB_FT_DEFAULT_STOPWORD;
-- Create custom stopword table
CREATE TABLE my_stopwords (value VARCHAR(30)) ENGINE = INNODB;
INSERT INTO my_stopwords (value) VALUES ('example'), ('custom'), ('stopword');
-- Set custom stopword table (in my.cnf or global variables)
SET GLOBAL innodb_ft_server_stopword_table = 'database_name/my_stopwords';
- Minimum word length: By default, MySQL ignores words shorter than 3 characters. Modify this with:
-- In my.cnf configuration file
ft_min_word_len=2 # For MyISAM
innodb_ft_min_token_size=2 # For InnoDB
After changing these settings, you must rebuild your full-text indexes:
REPAIR TABLE articles QUICK; # For MyISAM
ALTER TABLE articles DROP INDEX ft_title_content;
ALTER TABLE articles ADD FULLTEXT INDEX ft_title_content (title, content);
- Stemming: MySQL doesn't provide built-in stemming (reducing words to their root form), but you can implement partial solutions using the wildcard operator in boolean mode (
optimiz*
to match "optimize", "optimizing", etc.).
Full-Text Index Limitations
- Cannot use full-text indexes for
ORDER BY
orGROUP BY
- Cannot combine full-text searches with other conditions using indexes
- Full-text search does not support regex patterns
- InnoDB full-text indexes have different behaviors than MyISAM
- Limited language support for word boundary detection
Summary
MySQL full-text indexes provide a powerful way to implement text search functionality in your applications:
- Fast text searching: Much more efficient than
LIKE
queries for large text data - Flexible search modes: Natural language, boolean, and query expansion options
- Relevance ranking: Results ordered by importance to the search query
- Multiple column indexing: Can search across several text fields simultaneously
By implementing full-text indexes properly, you can dramatically improve the search experience in your applications and efficiently handle text-based queries even as your data grows.
Additional Resources and Exercises
Resources
Exercises
-
Basic Full-Text Search: Create a table with articles containing titles and content. Implement a full-text search to find articles about specific topics.
-
Boolean Mode Practice: Experiment with different boolean operators to refine search results. Try combining
+
,-
, and phrase searches. -
Relevance Ranking: Create a query that displays search results with their relevance scores, and experiment with how different combinations of words affect the ranking.
-
Search Optimization: Compare the performance of full-text search against
LIKE
queries usingEXPLAIN
to analyze the query plans. -
Advanced Application: Build a product search feature for an e-commerce database that uses full-text search across product names, descriptions, and categories.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)