第4章:创建第一个全文索引

4.1 索引创建的基本流程

4.1.1 创建索引的完整流程

创建一个 Whoosh 全文索引通常包含以下步骤:

1. 导入必要的模块
   ↓
2. 定义 Schema(字段结构)
   ↓
3. 指定索引目录
   ↓
4. 创建索引对象
   ↓
5. 获取 Writer 对象
   ↓
6. 添加文档数据
   ↓
7. 提交(commit)保存索引

4.1.2 目录准备

索引需要存储在文件系统中,因此需要先准备好目录:

import os

# 确保索引目录存在
index_dir = "my_index"
if not os.path.exists(index_dir):
    os.makedirs(index_dir)

4.1.3 创建与打开索引

Whoosh 提供两种方法操作索引:

1. 创建新索引

from whoosh.index import create_in

ix = create_in(index_dir, schema)

2. 打开已有索引

from whoosh.index import open_dir

ix = open_dir(index_dir)

3. 检查索引是否存在

from whoosh.index import exists_in

if exists_in(index_dir):
    ix = open_dir(index_dir)
else:
    ix = create_in(index_dir, schema)

4.2 定义Schema:文本、ID、日期、数值字段

4.2.1 Schema 设计原则

在设计 Schema 时,需要考虑:

  1. 存储需求:哪些字段需要直接读取?
  2. 索引需求:哪些字段需要参与搜索?
  3. 唯一性:哪些字段需要唯一约束?
  4. 性能影响:存储和索引的字段数量会影响性能

4.2.2 常用 Schema 定义

示例1:简单文章索引

from whoosh.fields import Schema, TEXT, ID

schema = Schema(
    title=TEXT(stored=True),      # 标题:存储+索引
    content=TEXT(stored=False),     # 内容:仅索引
    path=ID(stored=True, unique=True)  # 路径:唯一标识
)

示例2:商品搜索索引

from whoosh.fields import Schema, TEXT, ID, NUMERIC, KEYWORD

schema = Schema(
    name=TEXT(stored=True),                    # 商品名称
    description=TEXT(stored=False),               # 商品描述
    sku=ID(stored=True, unique=True),           # 商品SKU
    price=NUMERIC(stored=True, numtype=float),   # 价格
    category=KEYWORD(stored=True)               # 分类
)

示例3:日志文件索引

from whoosh.fields import Schema, TEXT, ID, NUMERIC

schema = Schema(
    timestamp=NUMERIC(stored=True, numtype=int),  # 时间戳
    level=KEYWORD(stored=True),                   # 日志级别
    message=TEXT(stored=True),                     # 日志内容
    file_path=ID(stored=True, unique=True)         # 文件路径
)

4.2.3 字段类型选择指南

字段类型 适用场景 是否分词 是否存储 常用参数
TEXT 全文搜索内容 可选 stored, analyzer
ID 唯一标识 通常存储 stored, unique
KEYWORD 标签、分类 可选 stored, commas
NUMERIC 价格、数量、时间戳 可选 stored, numtype, sortable

4.3 向索引添加文档数据

4.3.1 获取 Writer 对象

添加文档需要先获取 Writer 对象:

writer = ix.writer()

4.3.2 添加单个文档

writer.add_document(
    title="Python 编程入门",
    content="这是一本关于 Python 的入门教程",
    path="/docs/python.txt"
)

4.3.3 批量添加文档

批量添加可以提高性能:

documents = [
    {"title": "文档1", "content": "内容1", "path": "/doc1.txt"},
    {"title": "文档2", "content": "内容2", "path": "/doc2.txt"},
    {"title": "文档3", "content": "内容3", "path": "/doc3.txt"},
]

for doc in documents:
    writer.add_document(**doc)

4.3.4 提交与回滚

提交更改

writer.commit()  # 保存所有更改

取消更改

writer.cancel()  # 回滚,不保存

4.3.5 字符串编码注意事项

在 Python 3 中,确保使用 Unicode 字符串:

# ✅ 正确
writer.add_document(title=u"中文标题", content=u"中文内容")

# ❌ 错误(如果文件不是 UTF-8 编码)
with open("file.txt") as f:
    content = f.read()  # 可能不是 Unicode
    writer.add_document(content=content)

正确处理文件编码

with open("file.txt", "r", encoding="utf-8") as f:
    content = f.read()
    writer.add_document(content=content)

4.4 保存与打开索引文件

4.4.1 索引文件结构

创建索引后,会在指定目录生成多个文件:

my_index/
├─ _0.toc         # 段目录信息
├─ _0.terms       # 词项字典
├─ _0.postings    # 倒排列表
├─ _0.stored      # 存储字段
├─ _0.fieldlengths # 字段长度
└─ _0.lock        # 写锁(临时文件)

4.4.2 索引的持久化

调用 writer.commit() 后,索引会被持久化到磁盘:

writer = ix.writer()
writer.add_document(...)
writer.commit()  # 此时刻索引被保存到磁盘

4.4.3 打开已有索引

from whoosh.index import open_dir

# 打开索引
ix = open_dir("my_index")

# 使用搜索器
with ix.searcher() as searcher:
    results = searcher.search(query)

4.4.4 索引版本与段

Whoosh 使用段(Segments)管理索引:

  • 段(Segment):索引的不可变单元
  • 合并(Merging):定期合并多个段优化性能

查看段信息

ix = open_dir("my_index")
print("段数量:", ix.doc_count())
print("文档数量:", ix.doc_count_all())

4.4.5 索引锁定

当有 Writer 操作时,索引会被锁定:

# 检查是否被锁定
from whoosh.index import open_dir
try:
    ix = open_dir("my_index")
    writer = ix.writer()
except Exception as e:
    print(f"索引可能被锁定: {e}")

4.5 实战示例

4.5.1 完整示例:创建简单的文档索引

from whoosh.index import create_in, exists_in, open_dir
from whoosh.fields import Schema, TEXT, ID
import os

# 1. 准备目录
index_dir = "document_index"
if not os.path.exists(index_dir):
    os.makedirs(index_dir)

# 2. 定义 Schema
schema = Schema(
    title=TEXT(stored=True),
    content=TEXT(stored=False),
    path=ID(stored=True, unique=True)
)

# 3. 创建或打开索引
if exists_in(index_dir):
    ix = open_dir(index_dir)
else:
    ix = create_in(index_dir, schema)

# 4. 添加文档
writer = ix.writer()
writer.add_document(
    title="Whoosh 快速入门",
    content="Whoosh 是一个纯 Python 实现的全文检索库",
    path="/docs/whoosh.txt"
)
writer.add_document(
    title="Python 基础教程",
    content="Python 是一门简洁而强大的编程语言",
    path="/docs/python.txt"
)
writer.commit()

print("索引创建完成!")

4.5.2 实战练习

练习1:创建一个包含日期字段的索引

  • 添加 created 字段(DATETIME 类型)
  • 为每篇文档添加创建时间

练习2:批量导入多个文件

  • 读取一个文件夹下的所有 .txt 文件
  • 将每个文件的内容添加到索引

练习3:检查索引文件

  • 查看生成的索引目录结构
  • 打印出每个文件的大小

本章小结

本章我们学习了:

  1. 索引创建流程:从定义 Schema 到提交索引的完整步骤
  2. Schema 设计:如何根据业务需求定义字段类型
  3. 文档添加:单个和批量添加文档的方法
  4. 索引持久化:索引文件的保存、打开和管理

通过本章的学习,你应该能够独立创建一个完整的 Whoosh 索引,为后续的查询操作打下基础。

在下一章中,我们将学习如何执行基本的检索操作,包括各种查询类型和查询解析器的使用。

« 上一篇 核心概念解析 下一篇 » 基本检索操作