16
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

image.png

ElasticsearchやSolrなどの検索エンジンを導入するほどではないが、単純なLIKE検索では物足りない...そんな時に活躍するのがMySQLのFULLTEXT INDEXです。この記事では、Google Colabを使って実際に手を動かしながら、FULLTEXT INDEXの基本的な使い方を学びます。

環境セットアップ

Google ColabでMySQLを動かしてみましょう。

# MySQL関連パッケージのインストール
!apt-get update
!apt-get install -y mysql-server
!pip install mysql-connector-python

# MySQLサービス開始
!service mysql start

# rootパスワード設定
!mysql -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';"
import mysql.connector
from mysql.connector import Error

# データベース接続
def create_connection():
    try:
        connection = mysql.connector.connect(
            host='localhost',
            database='test_db',
            user='root',
            password='password'
        )
        return connection
    except Error as e:
        print(f"Error: {e}")
        return None

# データベース作成
connection = mysql.connector.connect(
    host='localhost',
    user='root',
    password='password'
)
cursor = connection.cursor()
cursor.execute("CREATE DATABASE IF NOT EXISTS test_db")
connection.commit()

サンプルデータの準備

ブログ記事を想定したテーブルを作成します。

-- テーブル作成
CREATE TABLE articles (
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(255) NOT NULL,
    content TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FULLTEXT(title, content) WITH PARSER ngram  -- 日本語対応のN-gramパーサーを使用
);

-- サンプルデータ挿入
INSERT INTO articles (title, content) VALUES
('Python入門', 'Pythonは初心者にも優しいプログラミング言語です。機械学習やWebアプリケーション開発に広く使われています。'),
('JavaScript基礎', 'JavaScriptはWebブラウザで動作するプログラミング言語です。ReactやVue.jsなどのフレームワークが人気です。'),
('データベース設計', 'データベース設計は重要な技術です。正規化やインデックスについて理解しましょう。'),
('機械学習入門', '機械学習はPythonのscikit-learnライブラリを使うと簡単に始められます。分類や回帰の基本を学びましょう。'),
('Web開発の基本', 'Web開発にはHTML、CSS、JavaScriptの知識が必要です。フロントエンドとバックエンドの理解も重要です。');
# Python実行例
connection = create_connection()
cursor = connection.cursor()

# テーブル作成とデータ挿入
create_table_query = """
CREATE TABLE IF NOT EXISTS articles (
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(255) NOT NULL,
    content TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FULLTEXT(title, content) WITH PARSER ngram
)
"""

cursor.execute(create_table_query)

# サンプルデータ
articles_data = [
    ('Python入門', 'Pythonは初心者にも優しいプログラミング言語です。機械学習やWebアプリケーション開発に広く使われています。'),
    ('JavaScript基礎', 'JavaScriptはWebブラウザで動作するプログラミング言語です。ReactやVue.jsなどのフレームワークが人気です。'),
    ('データベース設計', 'データベース設計は重要な技術です。正規化やインデックスについて理解しましょう。'),
    ('機械学習入門', '機械学習はPythonのscikit-learnライブラリを使うと簡単に始められます。分類や回帰の基本を学びましょう。'),
    ('Web開発の基本', 'Web開発にはHTML、CSS、JavaScriptの知識が必要です。フロントエンドとバックエンドの理解も重要です。')
]

cursor.executemany("INSERT INTO articles (title, content) VALUES (%s, %s)", articles_data)
connection.commit()

日本語検索の設定

MySQLで日本語検索を行う場合は、N-gramパーサーの設定が重要です。

-- N-gramのトークンサイズ設定確認
SHOW VARIABLES LIKE 'ngram_token_size';

-- 必要に応じて設定変更(my.cnfで設定推奨)
-- SET GLOBAL ngram_token_size = 2;
# 日本語検索の動作確認
def test_japanese_search():
    # 単語検索
    print("=== 単語検索テスト ===")
    cursor.execute("SELECT title FROM articles WHERE MATCH(title, content) AGAINST('Python')")
    results = cursor.fetchall()
    print(f"'Python'の検索結果: {len(results)}")
    
    # 日本語検索
    cursor.execute("SELECT title FROM articles WHERE MATCH(title, content) AGAINST('機械学習')")
    results = cursor.fetchall()
    print(f"'機械学習'の検索結果: {len(results)}")
    
    # 短い日本語検索
    cursor.execute("SELECT title FROM articles WHERE MATCH(title, content) AGAINST('入門')")
    results = cursor.fetchall()
    print(f"'入門'の検索結果: {len(results)}")

test_japanese_search()

FULLTEXT検索の基本

image.png

image.png

1. MATCH() AGAINST()の基本構文

-- 基本的な検索
SELECT * FROM articles 
WHERE MATCH(title, content) AGAINST('Python');

-- 複数キーワード検索
SELECT * FROM articles 
WHERE MATCH(title, content) AGAINST('Python 機械学習');

2. 検索モードの種類

# 自然言語モード(デフォルト)
def natural_search(keyword):
    query = "SELECT id, title FROM articles WHERE MATCH(title, content) AGAINST(%s)"
    cursor.execute(query, (keyword,))
    return cursor.fetchall()

# ブール検索モード
def boolean_search(keyword):
    query = "SELECT id, title FROM articles WHERE MATCH(title, content) AGAINST(%s IN BOOLEAN MODE)"
    cursor.execute(query, (keyword,))
    return cursor.fetchall()

# 検索実行例
print("自然言語検索 'Python':")
results = natural_search('Python')
for row in results:
    print(f"ID: {row[0]}, Title: {row[1]}")

print("\nブール検索 '+Python +機械学習':")
results = boolean_search('+Python +機械学習')
for row in results:
    print(f"ID: {row[0]}, Title: {row[1]}")

3. ブール検索の演算子

-- AND検索(両方含む)
SELECT * FROM articles 
WHERE MATCH(title, content) AGAINST('+Python +機械学習' IN BOOLEAN MODE);

-- OR検索(どちらか含む)
SELECT * FROM articles 
WHERE MATCH(title, content) AGAINST('Python JavaScript' IN BOOLEAN MODE);

-- NOT検索(除外)
SELECT * FROM articles 
WHERE MATCH(title, content) AGAINST('+プログラミング -JavaScript' IN BOOLEAN MODE);

-- フレーズ検索
SELECT * FROM articles 
WHERE MATCH(title, content) AGAINST('"Web開発"' IN BOOLEAN MODE);

関連度スコアの活用

# 関連度スコア付きの検索
def search_with_score(keyword):
    query = """
    SELECT id, title, 
           MATCH(title, content) AGAINST(%s) as score
    FROM articles 
    WHERE MATCH(title, content) AGAINST(%s)
    ORDER BY score DESC
    """
    cursor.execute(query, (keyword, keyword))
    return cursor.fetchall()

# 実行例
print("関連度スコア付き検索 'プログラミング':")
results = search_with_score('プログラミング')
for row in results:
    print(f"ID: {row[0]}, Title: {row[1]}, Score: {row[2]:.4f}")

実践的な検索機能の実装

class ArticleSearch:
    def __init__(self, connection):
        self.connection = connection
        self.cursor = connection.cursor()
    
    def search(self, keyword, limit=10):
        """基本検索"""
        query = """
        SELECT id, title, content,
               MATCH(title, content) AGAINST(%s) as score
        FROM articles 
        WHERE MATCH(title, content) AGAINST(%s)
        ORDER BY score DESC
        LIMIT %s
        """
        self.cursor.execute(query, (keyword, keyword, limit))
        return self.cursor.fetchall()
    
    def advanced_search(self, keyword, min_score=0.0):
        """関連度スコアによる絞り込み"""
        query = """
        SELECT id, title, content,
               MATCH(title, content) AGAINST(%s) as score
        FROM articles 
        WHERE MATCH(title, content) AGAINST(%s)
        HAVING score > %s
        ORDER BY score DESC
        """
        self.cursor.execute(query, (keyword, keyword, min_score))
        return self.cursor.fetchall()

# 使用例
searcher = ArticleSearch(connection)

print("=== 基本検索 ===")
results = searcher.search('Python')
for row in results:
    print(f"Title: {row[1]}")
    print(f"Score: {row[3]:.4f}")
    print("---")

print("\n=== 高精度検索 ===")
results = searcher.advanced_search('機械学習', min_score=0.1)
for row in results:
    print(f"Title: {row[1]}")
    print(f"Score: {row[3]:.4f}")
    print("---")

パフォーマンス比較

import time

def compare_performance():
    iterations = 100  # 複数回実行して平均を取る
    
    # LIKE検索
    start_time = time.time()
    for _ in range(iterations):
        cursor.execute("SELECT * FROM articles WHERE title LIKE %s OR content LIKE %s", ('%Python%', '%Python%'))
        like_results = cursor.fetchall()
    like_time = time.time() - start_time
    
    # FULLTEXT検索
    start_time = time.time()
    for _ in range(iterations):
        cursor.execute("SELECT * FROM articles WHERE MATCH(title, content) AGAINST(%s)", ('Python',))
        fulltext_results = cursor.fetchall()
    fulltext_time = time.time() - start_time
    
    print(f"LIKE検索: {like_time:.6f}秒 (平均: {like_time/iterations:.6f}秒)")
    print(f"FULLTEXT検索: {fulltext_time:.6f}秒 (平均: {fulltext_time/iterations:.6f}秒)")
    print(f"LIKE結果数: {len(like_results)}件, FULLTEXT結果数: {len(fulltext_results)}")

compare_performance()

📝 実測結果例(8件のサンプルデータ):

LIKE検索: 0.046606秒 (平均: 0.000466秒)
FULLTEXT検索: 0.063967秒 (平均: 0.000640秒)

📝 実測結果の解釈:

小さなデータセット(8件)での結果例:

LIKE検索: 0.059秒 (平均: 0.000590秒)
FULLTEXT検索: 0.100秒 (平均: 0.001004秒)

なぜFULLTEXT検索が遅い?

  • インデックスの初期化コスト
  • 関連度スコア計算のオーバーヘッド
  • 小データではLIKE検索の方が単純で高速

実際のプロダクションでは:

  • 1万件以上:FULLTEXT検索が数倍〜数十倍高速
  • 10万件以上:FULLTEXT検索が圧倒的に有利
  • 複雑な検索条件:FULLTEXT検索でないと実用的でない

まとめ

MySQL の FULLTEXT INDEX を使えば、追加ミドルウェアなしで高速かつ関連度順の検索が実装できます。日本語対応には N-gram パーサが必須で、最小文字数や ngram_token_size を調整しないと精度が落ちるほか、インデックス肥大化にも注意が必要です。

シンプルな検索機能であれば、まずはFULLTEXT INDEXから始めてみるといいかもしれません・・・。

参考情報

実装例

google colab での動作確認した

# MySQL FULLTEXT INDEX 検証用 Google Colab ノートブック

# =============================================================================
# セル1: 環境セットアップ
# =============================================================================

# MySQL関連パッケージのインストール
!apt-get update > /dev/null 2>&1
!apt-get install -y mysql-server > /dev/null 2>&1
!pip install mysql-connector-python > /dev/null 2>&1

print("✅ パッケージインストール完了")

# MySQLサービス開始
!service mysql start > /dev/null 2>&1
print("✅ MySQLサービス開始")

# rootパスワード設定
!mysql -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';" > /dev/null 2>&1
print("✅ rootパスワード設定完了")

# =============================================================================
# セル2: データベース接続とセットアップ
# =============================================================================

import mysql.connector
from mysql.connector import Error
import time

def create_connection():
    """データベース接続を作成"""
    try:
        connection = mysql.connector.connect(
            host='localhost',
            database='test_db',
            user='root',
            password='password'
        )
        return connection
    except Error as e:
        print(f"Error: {e}")
        return None

# データベース作成
try:
    connection = mysql.connector.connect(
        host='localhost',
        user='root',
        password='password'
    )
    cursor = connection.cursor()
    cursor.execute("CREATE DATABASE IF NOT EXISTS test_db")
    connection.commit()
    connection.close()
    print("✅ データベース作成完了")
except Error as e:
    print(f"❌ データベース作成エラー: {e}")

# =============================================================================
# セル3: N-gram設定の確認
# =============================================================================

# N-gram設定の確認
connection = create_connection()
if connection:
    cursor = connection.cursor()
    
    print("=== MySQL N-gram設定確認 ===")
    cursor.execute("SHOW VARIABLES LIKE 'ngram_token_size'")
    result = cursor.fetchone()
    if result:
        print(f"ngram_token_size: {result[1]}")
    else:
        print("N-gramパーサーが利用できない可能性があります")
    
    cursor.execute("SELECT VERSION()")
    version = cursor.fetchone()
    print(f"MySQL Version: {version[0]}")
    
    connection.close()

# =============================================================================
# セル4: テーブル作成とデータ挿入
# =============================================================================

connection = create_connection()
if connection:
    cursor = connection.cursor()
    
    # 既存テーブルを削除
    cursor.execute("DROP TABLE IF EXISTS articles")
    
    # N-gramパーサーを使用したテーブル作成
    create_table_query = """
    CREATE TABLE articles (
        id INT AUTO_INCREMENT PRIMARY KEY,
        title VARCHAR(255) NOT NULL,
        content TEXT NOT NULL,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        FULLTEXT(title, content) WITH PARSER ngram
    ) ENGINE=InnoDB
    """
    
    try:
        cursor.execute(create_table_query)
        print("✅ N-gramパーサー付きテーブル作成成功")
    except Error as e:
        print(f"❌ N-gramパーサーでのテーブル作成失敗: {e}")
        print("デフォルトパーサーでテーブルを作成します...")
        
        # フォールバック: デフォルトパーサー
        fallback_query = """
        CREATE TABLE articles (
            id INT AUTO_INCREMENT PRIMARY KEY,
            title VARCHAR(255) NOT NULL,
            content TEXT NOT NULL,
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
            FULLTEXT(title, content)
        ) ENGINE=InnoDB
        """
        cursor.execute(fallback_query)
        print("✅ デフォルトパーサーでテーブル作成成功")
    
    # サンプルデータ
    articles_data = [
        ('Python入門', 'Pythonは初心者にも優しいプログラミング言語です。機械学習やWebアプリケーション開発に広く使われています。データ分析にも最適です。'),
        ('JavaScript基礎', 'JavaScriptはWebブラウザで動作するプログラミング言語です。ReactやVue.jsなどのフレームワークが人気です。Node.jsでサーバーサイド開発も可能です。'),
        ('データベース設計', 'データベース設計は重要な技術です。正規化やインデックスについて理解しましょう。MySQLやPostgreSQLが広く使われています。'),
        ('機械学習入門', '機械学習はPythonのscikit-learnライブラリを使うと簡単に始められます。分類や回帰の基本を学びましょう。TensorFlowやPyTorchも人気です。'),
        ('Web開発の基本', 'Web開発にはHTML、CSS、JavaScriptの知識が必要です。フロントエンドとバックエンドの理解も重要です。レスポンシブデザインも学びましょう。'),
        ('AI技術の現在', '人工知能技術は急速に発展しています。ChatGPTのような大規模言語モデルが注目されています。'),
        ('データ分析手法', 'データ分析には統計学の知識が重要です。Pythonのpandasやnumpyライブラリを活用しましょう。'),
        ('クラウド開発', 'AWSやGoogle Cloud Platformなどのクラウドサービスが開発の主流になっています。')
    ]
    
    cursor.executemany("INSERT INTO articles (title, content) VALUES (%s, %s)", articles_data)
    connection.commit()
    
    print(f"{len(articles_data)}件のサンプルデータを挿入しました")
    connection.close()

# =============================================================================
# セル5: 基本的な検索テスト
# =============================================================================

def test_basic_search():
    connection = create_connection()
    if not connection:
        return
    
    cursor = connection.cursor()
    
    print("=== 基本検索テスト ===")
    
    # テスト1: 英語キーワード
    print("\n1. 英語キーワード検索 'Python'")
    cursor.execute("SELECT id, title FROM articles WHERE MATCH(title, content) AGAINST('Python')")
    results = cursor.fetchall()
    print(f"   結果数: {len(results)}")
    for row in results:
        print(f"   - {row[1]}")
    
    # テスト2: 日本語キーワード
    print("\n2. 日本語キーワード検索 '機械学習'")
    cursor.execute("SELECT id, title FROM articles WHERE MATCH(title, content) AGAINST('機械学習')")
    results = cursor.fetchall()
    print(f"   結果数: {len(results)}")
    for row in results:
        print(f"   - {row[1]}")
    
    # テスト3: 短い日本語キーワード
    print("\n3. 短い日本語キーワード検索 '入門'")
    cursor.execute("SELECT id, title FROM articles WHERE MATCH(title, content) AGAINST('入門')")
    results = cursor.fetchall()
    print(f"   結果数: {len(results)}")
    for row in results:
        print(f"   - {row[1]}")
    
    # テスト4: 複合キーワード
    print("\n4. 複合キーワード検索 'Web JavaScript'")
    cursor.execute("SELECT id, title FROM articles WHERE MATCH(title, content) AGAINST('Web JavaScript')")
    results = cursor.fetchall()
    print(f"   結果数: {len(results)}")
    for row in results:
        print(f"   - {row[1]}")
    
    connection.close()

test_basic_search()

# =============================================================================
# セル6: 関連度スコア付き検索
# =============================================================================

def test_score_search():
    connection = create_connection()
    if not connection:
        return
    
    cursor = connection.cursor()
    
    print("=== 関連度スコア付き検索 ===")
    
    query = """
    SELECT id, title, 
           MATCH(title, content) AGAINST(%s) as score
    FROM articles 
    WHERE MATCH(title, content) AGAINST(%s)
    ORDER BY score DESC
    """
    
    keywords = ['Python', '開発', 'データ']
    
    for keyword in keywords:
        print(f"\n🔍 キーワード: '{keyword}'")
        cursor.execute(query, (keyword, keyword))
        results = cursor.fetchall()
        
        if results:
            for row in results:
                print(f"   Score: {row[2]:.4f} - {row[1]}")
        else:
            print("   結果なし")
    
    connection.close()

test_score_search()

# =============================================================================
# セル7: ブール検索テスト
# =============================================================================

def test_boolean_search():
    connection = create_connection()
    if not connection:
        return
    
    cursor = connection.cursor()
    
    print("=== ブール検索テスト ===")
    
    # AND検索
    print("\n1. AND検索 '+Python +機械学習'")
    cursor.execute("SELECT title FROM articles WHERE MATCH(title, content) AGAINST('+Python +機械学習' IN BOOLEAN MODE)")
    results = cursor.fetchall()
    print(f"   結果数: {len(results)}")
    for row in results:
        print(f"   - {row[0]}")
    
    # OR検索
    print("\n2. OR検索 'Python JavaScript'")
    cursor.execute("SELECT title FROM articles WHERE MATCH(title, content) AGAINST('Python JavaScript' IN BOOLEAN MODE)")
    results = cursor.fetchall()
    print(f"   結果数: {len(results)}")
    for row in results:
        print(f"   - {row[0]}")
    
    # NOT検索
    print("\n3. NOT検索 '+開発 -JavaScript'")
    cursor.execute("SELECT title FROM articles WHERE MATCH(title, content) AGAINST('+開発 -JavaScript' IN BOOLEAN MODE)")
    results = cursor.fetchall()
    print(f"   結果数: {len(results)}")
    for row in results:
        print(f"   - {row[0]}")
    
    # フレーズ検索
    print("\n4. フレーズ検索 '\"Web開発\"'")
    cursor.execute("SELECT title FROM articles WHERE MATCH(title, content) AGAINST('\"Web開発\"' IN BOOLEAN MODE)")
    results = cursor.fetchall()
    print(f"   結果数: {len(results)}")
    for row in results:
        print(f"   - {row[0]}")
    
    connection.close()

test_boolean_search()

# =============================================================================
# セル8: パフォーマンス比較
# =============================================================================

def performance_comparison():
    connection = create_connection()
    if not connection:
        return
    
    cursor = connection.cursor()
    
    print("=== パフォーマンス比較 ===")
    
    keyword = 'Python'
    iterations = 100
    
    # LIKE検索のテスト
    start_time = time.time()
    for _ in range(iterations):
        cursor.execute("SELECT id, title FROM articles WHERE title LIKE %s OR content LIKE %s", 
                      (f'%{keyword}%', f'%{keyword}%'))
        results = cursor.fetchall()
    like_time = time.time() - start_time
    like_count = len(results)
    
    # FULLTEXT検索のテスト
    start_time = time.time()
    for _ in range(iterations):
        cursor.execute("SELECT id, title FROM articles WHERE MATCH(title, content) AGAINST(%s)", (keyword,))
        results = cursor.fetchall()
    fulltext_time = time.time() - start_time
    fulltext_count = len(results)
    
    print(f"\n📊 {iterations}回実行の結果:")
    print(f"LIKE検索:")
    print(f"  - 実行時間: {like_time:.6f}")
    print(f"  - 平均時間: {like_time/iterations:.6f}")
    print(f"  - 結果数: {like_count}")
    
    print(f"\nFULLTEXT検索:")
    print(f"  - 実行時間: {fulltext_time:.6f}")
    print(f"  - 平均時間: {fulltext_time/iterations:.6f}")
    print(f"  - 結果数: {fulltext_count}")
    
    if like_time > 0:
        speedup = like_time / fulltext_time
        print(f"\n⚡ FULLTEXT検索は約{speedup:.2f}倍高速")
    
    connection.close()

performance_comparison()

# =============================================================================
# セル9: 実践的な検索クラス
# =============================================================================

class ArticleSearch:
    def __init__(self):
        self.connection = create_connection()
        if self.connection:
            self.cursor = self.connection.cursor()
    
    def search(self, keyword, limit=10):
        """基本検索"""
        if not self.connection:
            return []
        
        query = """
        SELECT id, title, content,
               MATCH(title, content) AGAINST(%s) as score
        FROM articles 
        WHERE MATCH(title, content) AGAINST(%s)
        ORDER BY score DESC
        LIMIT %s
        """
        self.cursor.execute(query, (keyword, keyword, limit))
        return self.cursor.fetchall()
    
    def advanced_search(self, keyword, min_score=0.0):
        """関連度スコアによる絞り込み"""
        if not self.connection:
            return []
        
        query = """
        SELECT id, title, content,
               MATCH(title, content) AGAINST(%s) as score
        FROM articles 
        WHERE MATCH(title, content) AGAINST(%s)
        HAVING score > %s
        ORDER BY score DESC
        """
        self.cursor.execute(query, (keyword, keyword, min_score))
        return self.cursor.fetchall()
    
    def boolean_search(self, keyword):
        """ブール検索"""
        if not self.connection:
            return []
        
        query = """
        SELECT id, title, content
        FROM articles 
        WHERE MATCH(title, content) AGAINST(%s IN BOOLEAN MODE)
        """
        self.cursor.execute(query, (keyword,))
        return self.cursor.fetchall()
    
    def close(self):
        if self.connection:
            self.connection.close()

# 使用例
print("=== 実践的な検索クラステスト ===")

searcher = ArticleSearch()

if searcher.connection:
    print("\n🔍 基本検索 'データ':")
    results = searcher.search('データ')
    for row in results:
        print(f"  Score: {row[3]:.4f} - {row[1]}")
    
    print("\n🔍 高精度検索 '開発' (min_score=0.1):")
    results = searcher.advanced_search('開発', min_score=0.1)
    for row in results:
        print(f"  Score: {row[3]:.4f} - {row[1]}")
    
    print("\n🔍 ブール検索 '+Python +データ':")
    results = searcher.boolean_search('+Python +データ')
    for row in results:
        print(f"  - {row[1]}")
    
    searcher.close()
else:
    print("❌ データベース接続に失敗しました")

# =============================================================================
# セル10: 検索結果の詳細表示
# =============================================================================

def detailed_search_demo():
    connection = create_connection()
    if not connection:
        return
    
    cursor = connection.cursor()
    
    print("=== 詳細検索デモ ===")
    
    keyword = input("検索キーワードを入力してください (例: Python, 機械学習, 開発): ") or "Python"
    
    query = """
    SELECT id, title, content,
           MATCH(title, content) AGAINST(%s) as score
    FROM articles 
    WHERE MATCH(title, content) AGAINST(%s)
    ORDER BY score DESC
    """
    
    cursor.execute(query, (keyword, keyword))
    results = cursor.fetchall()
    
    print(f"\n🔍 '{keyword}' の検索結果: {len(results)}")
    print("=" * 50)
    
    for i, row in enumerate(results, 1):
        print(f"\n{i}. {row[1]} (Score: {row[3]:.4f})")
        print("-" * 30)
        # 内容の最初の100文字を表示
        content_preview = row[2][:100] + "..." if len(row[2]) > 100 else row[2]
        print(f"内容: {content_preview}")
    
    if not results:
        print("該当する記事が見つかりませんでした。")
        print("他のキーワードを試してみてください。")
    
    connection.close()

# インタラクティブな検索デモ実行
detailed_search_demo()

print("\n✅ すべてのテストが完了しました!")
print("このノートブックでMySQLのFULLTEXT INDEXの動作を確認できました。")

出力例

✅ パッケージインストール完了
✅ MySQLサービス開始
✅ rootパスワード設定完了
✅ データベース作成完了
=== MySQL N-gram設定確認 ===
ngram_token_size: 2
MySQL Version: 8.0.42-0ubuntu0.22.04.1
✅ N-gramパーサー付きテーブル作成成功
✅ 8件のサンプルデータを挿入しました
=== 基本検索テスト ===

1. 英語キーワード検索 'Python'
   結果数: 3件
   - Python入門
   - 機械学習入門
   - データ分析手法

2. 日本語キーワード検索 '機械学習'
   結果数: 2件
   - 機械学習入門
   - Python入門

3. 短い日本語キーワード検索 '入門'
   結果数: 2件
   - Python入門
   - 機械学習入門

4. 複合キーワード検索 'Web JavaScript'
   結果数: 5件
   - JavaScript基礎
   - Web開発の基本
   - Python入門
   - 機械学習入門
   - AI技術の現在
=== 関連度スコア付き検索 ===

🔍 キーワード: 'Python'
   Score: 1.4516 - Python入門
   Score: 1.0887 - 機械学習入門
   Score: 0.9072 - データ分析手法

🔍 キーワード: '開発'
   Score: 0.1812 - Web開発の基本
   Score: 0.1812 - クラウド開発
   Score: 0.0906 - Python入門
   Score: 0.0906 - JavaScript基礎

🔍 キーワード: 'データ'
   Score: 0.7258 - データベース設計
   Score: 0.7258 - データ分析手法
   Score: 0.3629 - Python入門
=== ブール検索テスト ===

1. AND検索 '+Python +機械学習'
   結果数: 2件
   - 機械学習入門
   - Python入門

2. OR検索 'Python JavaScript'
   結果数: 5件
   - Python入門
   - JavaScript基礎
   - 機械学習入門
   - データ分析手法
   - Web開発の基本

3. NOT検索 '+開発 -JavaScript'
   結果数: 2件
   - クラウド開発
   - Python入門

4. フレーズ検索 '"Web開発"'
   結果数: 1件
   - Web開発の基本
=== パフォーマンス比較 ===

📊 100回実行の結果:
LIKE検索:
  - 実行時間: 0.059043秒
  - 平均時間: 0.000590秒
  - 結果数: 3件

FULLTEXT検索:
  - 実行時間: 0.100407秒
  - 平均時間: 0.001004秒
  - 結果数: 3件

⚡ FULLTEXT検索は約0.59倍高速
=== 実践的な検索クラステスト ===

🔍 基本検索 'データ':
  Score: 0.7258 - データベース設計
  Score: 0.7258 - データ分析手法
  Score: 0.3629 - Python入門

🔍 高精度検索 '開発' (min_score=0.1):
  Score: 0.1812 - Web開発の基本
  Score: 0.1812 - クラウド開発

🔍 ブール検索 '+Python +データ':
  - Python入門
  - データ分析手法
=== 詳細検索デモ ===
検索キーワードを入力してください (例: Python, 機械学習, 開発): 開発

🔍 '開発' の検索結果: 4件
==================================================

1. Web開発の基本 (Score: 0.1812)
------------------------------
内容: Web開発にはHTML、CSS、JavaScriptの知識が必要です。フロントエンドとバックエンドの理解も重要です。レスポンシブデザインも学びましょう。

2. クラウド開発 (Score: 0.1812)
------------------------------
内容: AWSやGoogle Cloud Platformなどのクラウドサービスが開発の主流になっています。

3. Python入門 (Score: 0.0906)
------------------------------
内容: Pythonは初心者にも優しいプログラミング言語です。機械学習やWebアプリケーション開発に広く使われています。データ分析にも最適です。

4. JavaScript基礎 (Score: 0.0906)
------------------------------
内容: JavaScriptはWebブラウザで動作するプログラミング言語です。ReactやVue.jsなどのフレームワークが人気です。Node.jsでサーバーサイド開発も可能です。

✅ すべてのテストが完了しました!
このノートブックでMySQLのFULLTEXT INDEXの動作を確認できました。

image.png

参考情報

16
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?