[Whoosh 搜索引擎之 四 ] 模式设计

模式设计

关于模式和字段

模式指定索引中文档的字段。

每个文档可以有多个字段,比如标题、内容、url、日期等。

一些字段可以被索引,一些字段可以与文档一起存储,以便字段值在搜索结果中可用。一些字段将被索引和存储。

模式是文档中所有可能字段的集合。每个单独的文档可能只使用模式中可用字段的一个子集。

例如,用于索引电子邮件的简单模式可能包含 from_addrto_addrsubjectbodyattachments 等字段 ,其中 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,tf

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 根本不使用术语向量,但它们可供希望实现自己的字段类型的专家用户使用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,313评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,369评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,916评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,333评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,425评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,481评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,491评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,268评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,719评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,004评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,179评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,832评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,510评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,153评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,402评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,045评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,071评论 2 352

推荐阅读更多精彩内容