模式设计
关于模式和字段
模式指定索引中文档的字段。
每个文档可以有多个字段,比如标题、内容、url、日期等。
一些字段可以被索引,一些字段可以与文档一起存储,以便字段值在搜索结果中可用。一些字段将被索引和存储。
模式是文档中所有可能字段的集合。每个单独的文档可能只使用模式中可用字段的一个子集。
例如,用于索引电子邮件的简单模式可能包含 from_addr
、to_addr
、subject
、body
和 attachments
等字段 ,其中 attachments
字段列出了电子邮件附件的名称。对于没有附件的电子邮件,您可以省略附件字段。
内置字段类型
Whoosh 提供了一些有用的预定义字段类型:
whoosh.fields.TEXT
这种类型用于正文。它索引(并可选地存储)文本并存储术语位置以允许短语搜索。
TEXT
字段默认使用 StandardAnalyzer
分析器。要指定不同的分析器,请在构造函数中使用 analyzer
参数,例如 TEXT(analyzer=analysis.StemmingAnalyzer())
. 请参阅 关于分析器。
默认情况下,TEXT
字段存储每个索引术语的位置信息,以允许您搜索短语。如果您不需要能够在文本字段中搜索短语,您可以关闭存储术语位置以节省空间。使用 TEXT(phrase=False)
。
默认情况下,TEXT
不存储字段。通常您不想将正文存储在搜索索引中。通常您可以根据搜索结果阅读或链接到索引文档本身,因此您不需要将它们的文本存储在搜索索引中。但是,在某些情况下它可能很有用(请参阅 如何创建突出显示的搜索结果摘录)。使用 TEXT(stored=True)
指定将文本存储在索引中。
whoosh.fields.KEYWORD
此字段类型专为以空格或逗号分隔的关键字而设计。这种类型是索引和可搜索的(并且可以选择存储)。为了节省空间,它不支持短语搜索。
要将字段的值存储在索引中,请在构造函数中使用 stored=True
。要在索引关键字之前自动将关键字小写,请使用 lowercase=True
.
默认情况下,关键字以空格分隔。要改为用逗号分隔关键字(以允许关键字包含空格),请使用 commas=True
.
如果您的用户需要使用 KEYWORD
字段进行搜索,请使用 scorable=True
.
whoosh.fields.ID
字段 ID
类型只是将字段的整个值作为一个单元进行索引(并可选地存储)也就是说,它不会将其分解为单独的术语。这种类型的字段不存储频率信息,因此它非常紧凑,但对于评分不是很有用。
将 ID
用于 URL 或路径(文档的 URL 或文件路径)、日期、类别等字段,这些字段的值必须作为一个整体来处理,并且每个文档只能有一个值使用此类型。
默认情况下,ID
字段不存储。使用 ID(stored=True)
指定字段的值应与文档一起存储以用于搜索结果。例如,您可能希望存储 URL 字段的值,以便您可以在搜索结果中提供指向原始内容的链接。
whoosh.fields.STORED
该字段与文档一起存储,但未编入索引且不可搜索。这对于您希望在搜索结果中向用户显示但不需要能够搜索到的文档信息很有用。
whoosh.fields.NUMERIC
该字段以紧凑、可排序的格式存储整数、长整数或浮点数。
whoosh.fields.DATETIME
该字段以紧凑、可排序的格式存储日期时间对象。
whoosh.fields.BOOLEAN
这个简单的字段索引布尔值并允许用户搜索 yes
, no
, true
, false
, 1
, 0
,t
或 f
。
whoosh.fields.NGRAM
待定。
专家用户可以创建自己的字段类型。
创建模式
创建一个模式:
from whoosh.fields import Schema, TEXT, KEYWORD, ID, STORED
from whoosh.analysis import StemmingAnalyzer
schema = Schema(from_addr=ID(stored=True),
to_addr=ID(stored=True),
subject=TEXT(stored=True),
body=TEXT(analyzer=StemmingAnalyzer()),
tags=KEYWORD)
如果您不需要为预定义字段构造函数指定任何参数,则可以省略括号(例如,使用 fieldname=TEXT
替代 fieldname=TEXT()
)。Whoosh 将为您实例化该类。
或者,您可以使用 SchemaClass
基类以声明方式创建模式:
from whoosh.fields import SchemaClass, TEXT, KEYWORD, ID, STORED
class MySchema(SchemaClass):
path = ID(stored=True)
title = TEXT(stored=True)
content = TEXT
tags = KEYWORD
您可以将声明类传递给实例 create_in()
或 create_index()
代替 Schema
实例。
索引后修改架构
创建索引后,您可以使用 add_field()
和 remove_field()
方法向模式添加或删除字段。这些方法在 Writer
对象上:
writer = ix.writer()
writer.add_field("fieldname", fields.TEXT(stored=True))
writer.remove_field("content")
writer.commit()
(如果您要修改模式并使用同一编写器添加文档,则必须在添加任何文档之前调用 add_field()
或 remove_field
)
为了方便起见,这些方法也在 Index
对象上,但是当您在 Index
上调用它们时 ,Index
对象只是创建编写器,在其上调用相应的方法,然后提交,因此如果您想要添加或删除多个字段,自己创建编写器效率更高:
ix.add_field("fieldname", fields.KEYWORD)
在 filedb
后端,删除字段只是简单的从架构中删除该字段 —— 索引不会变小,有关该字段的数据将保留在索引中,直到您进行优化。优化将压缩索引,同时删除对已删除字段的引用:
writer = ix.writer()
writer.add_field("uuid", fields.ID(stored=True))
writer.remove_field("path")
writer.commit(optimize=True)
因为数据是以字段名存储在磁盘上的,所以在没有优化中间索引的情况下,不要添加与已删除字段同名的新字段:
writer = ix.writer()
writer.delete_field("path")
# 不要这样做!!!
writer.add_field("path", fields.KEYWORD)
(Whoosh 的未来版本可能会自动防止此错误。)
动态字段
动态字段让您可以将字段类型与任意 "glob"(包含*
、?
、/
、[abc]
等通配符的名称)类型字段相匹配。
您可以使用 add()
方法将动态字段添加到 glob
关键字设置为 True
的新模式:
schema = fields.Schema(...)
# 任何以 "_d" 结尾的字段名,都将被存储为 DATETIME 类型
schema.add("*_d", fields.DATETIME(stored=True), glob=True)
要在现有索引上设置动态字段,与添加常规字段相同,使用 IndexWriter.add_field
方法,但 glob
关键字参数需要设置为 True
:
writer = ix.writer()
writer.add_field("*_d", fields.DATETIME(stored=True), glob=True)
writer.commit()
要删除动态字段,请对以 glob 作为名称的字段使用 IndexWriter.remove_field()
方法 :
writer = ix.writer()
writer.remove_field("*_d")
writer.commit()
例如,要允许文档包含以 _id
结尾的任何字段名称并将其与 ID
字段类型相关联:
schema = fields.Schema(path=fields.ID)
schema.add("*_id", fields.ID, glob=True)
ix = index.create_in("myindex", schema)
w = ix.writer()
w.add_document(path=u"/a", test_id=u"alfa")
w.add_document(path=u"/b", class_id=u"MyClass")
# ...
w.commit()
qp = qparser.QueryParser("path", schema=schema)
q = qp.parse(u"test_id:alfa")
with ix.searcher() as s:
results = s.search(q)
高级模式设置
字段提升 (Field boosts)
您可以为字段指定字段提升。这是一个乘数,适用于在该字段中找到的任何术语的分数。例如,要使 title
字段中的术语得分是 body
字段中术语得分的两倍:
schema = Schema(title=TEXT(field_boost=2.0), body=TEXT)
字段类型
上面列出的预定义字段类型是 fields.FieldType
的子类。 FieldType
是一个非常简单的类。它的属性包含定义字段行为的信息。
属性 | 类型 | 描述 |
---|---|---|
format | fields.Format | 定义字段记录关于每个术语的信息类型,以及信息如何存储在磁盘上。 |
vector | fields.Format | 可选:如果已定义,则为该字段存储每个文档的前向索引信息的格式。 |
scorable | bool | 如果为 True ,则每个文档中字段存储在索引中的长度(术语的数量)。有点命名错误,因为字段长度不是所有评分所必需的。但是需要字段长度才能从 BM25F 获得正确的结果。 |
stored | bool | 如果为 True ,则此字段的值存储在索引中。 |
unique | bool | 如果为 True ,则当用户在 IndexWriter 上调用 document_update() 时,此字段的值可用于替换具有相同值的文档。 |
大多数预定义字段类型的构造函数都具有可让您自定义这些部分的参数。例如:
- 大多数预定义的字段类型都可以在构造函数中使用
FieldType.stored
参数。 -
TEXT()
构造函数可以使用analyzer
参数设置格式对象。
格式
对象 Format
定义字段记录关于每个术语的信息类型,以及信息如何在磁盘上存储。
例如,Existence
格式将存储这样的帖子 (postings):
Doc | |
---|---|
10 | |
20 | |
30 |
而 Positions
格式将存储这样的帖子:
Doc | Positions |
---|---|
10 | [1,5,23] |
20 | [45] |
30 | [7,12] |
索引代码将字段的 unicode
字符串传递给字段的 Format
对象。该 Format
对象调用其分析器(请参阅文本分析)将字符串分解为标记,然后对有关每个标记的信息进行编码。
Whoosh 附带以下预定义格式。
类名 | 描述 |
---|---|
Stored | 存储但未索引的字段的“null”格式。 |
Existence | 只记录词是否在文档中,而不存储词频。对于标识符字段(例如路径或 ID)和 “tag” 类型字段很有用,这些字段的频率应始终为 0 或 1 |
Frequency | 存储每个术语在每个文档中出现的次数。 |
Positions | 存储每个术语在每个文档中出现的次数以及出现的位置。 |
STORED
字段类型使用 Stored
格式(什么都不做,所以 STORED
字段没有索引)。
类型 ID
使用 Existence
格式。
类型 KEYWORD
使用 Frequency
格式。
类型 TEXT
如果使用 phrase=True
(默认值)实例化则使用 Positions
格式 ,如果 phrase=False
则使用 Frequency
格式.
此外,为方便专家用户而实现了以下格式,但目前尚未在 Whoosh 中使用:
类名 | 描述 |
---|---|
DocBoosts | 与 Existence 类似,但额外存储每个文档的提升(boosts) |
Characters | 与 Positions 类似,但额外存储每个术语的开始和结束字符索引 |
PositionBoosts | 与 Positions 类似,但额外存储每个位置的提升 |
CharacterBoosts | 与 Positions 类似,但额外存储每个术语和每个位置提升的开始和结束字符索引 |
向量
主索引是倒排索引。它将术语映射到它们出现的文档。存储前向索引(也称为词向量)有时也很有用,它将文档映射到出现在其中的术语。
例如,想象一个字段的倒排索引:
Term | Postings |
---|---|
apple | [(doc=1, freq=2), (doc=2, freq=5), (doc=3, freq=1)] |
bear | [(doc=2, freq=7)] |
相应的前向索引或词向量将是:
Doc | Postings |
---|---|
1 | [(text=apple, freq=2)] |
2 | [(text=apple, freq=5), (text='bear', freq=7)] |
3 | [(text=apple, freq=1)] |
如果设置 FieldType.vector
为一个 Format
对象,索引代码将使用该 Format
对象来存储有关每个文档中的术语的信息。目前默认情况下 Whoosh
根本不使用术语向量,但它们可供希望实现自己的字段类型的专家用户使用。