文本检索 关键词检索和
问候,
介绍
本周,我们开始构建Query对象。 查询可以检索部分文本
从图书馆。 我不希望用户自己建立查询,因为用户
犯错误。 取而代之的是,图书馆提供给用户一个简单的查询
请求参数。 库是这样的:
public Query getQuery(String query) throws QueryException {
return new QueryTxt(this, query);
}
用户输入一个字符串,然后再次获得一个查询。
但是什么是查询?
继续阅读。
查询界面
查询又是一个接口,它看起来像这样:
public interface Query {
public int size();
public List<BookMark> result();
}
size()方法返回具有正数的结果(段)数
与查询文字匹配。 result()方法返回书签列表
代表所有匹配的段落。
在内部,查询子系统使用此接口的扩展:
interface InternalQuery extends Query {
public BitSet evaluate();
}
如您所见,该界面将以前的界面扩展为一个
方法。 该validate()方法应该评估(原文如此)该查询。 这个
接口不是公共接口,因此用户只知道查询
接口本身。
result()接口方法的实现调用了validate()
方法仅一次; 所以无论result()方法本身是多少次
调用后,结果已经是“已知的”并且查询不需要
重新评估。
查询内部
简单查询只是一个简单的文本单词。 这个词在
库的WordMap(请参阅上一节),并使用BitSet
指出哪些段落包含该词:
private BitSet makeBitSet(int[] p) {
BitSet bs= new BitSet();
for (int i= 0; i < p.length; bs.set(p[i++]));
return bs;
}
给定int数组,这些数组代表以下段落的索引:
一个单词出现时,将构造一个BitSet并将相应的位设置为1。
如果您查看BitSet API文档,就会发现BitSet非常有用
方便工作:他们可以使用一种方法来操纵整个BitSet
调用,即它们可以“和”和“或”两个BitSet以及整个范围
可以设置或重置BitSet中的位。
那么,为什么我不首先在该WordMap中使用BitSet? 原因是,
BitSet可能比段落索引的简单数组大得多; 假设
仅在第65,000段中出现一个字。 一个BitSet将花费+-65,000位
(大约8KB),而一个元素int数组仅存储索引
数字65,000将采用单个32位int。
查询对象涉及的大多数工作是操纵BitSet,因为
他们很快就可以操纵。
查询语言
传递给库的字符串具有某种语法,即
查询。 这是一种非常简单的语言:最简单的查询是单个
单词如上一段所示。
查询可以在逻辑上“与”和“或”在一起。 “和”运算符具有
优先级高于“或”运算符,例如查询:
耶稣与上帝 魔鬼
列出其中出现耶稣和上帝两个单词的段落,或者只是
魔鬼这个词,或全部三个词。
请注意,符号“&”和“ |” 代表“和”和“或”运算符。
子查询可以通过用括号“(”和“)”括起来进行分组。
例如,以下查询给出的结果与上一个查询不同
查询确实:
耶稣与(上帝|恶魔)
如果这些段落包含耶稣这个词,则它们是查询的结果
连同“上帝或魔鬼”一词或其中两个词一起使用
可以在查询前面加上感叹号“!”来取反查询,因此
以下查询:
耶稣&!(上帝|魔鬼)
返回那些包含耶稣一词但不包含任何词的段落
神还是魔鬼。
'and'运算符还有很多:如果直接跟一个数字
(非负整数)该数字被视为“接近”; 这是
一个例子:
耶稣&10神
此查询返回包含(或两个词)的所有段落
耶稣或上帝,只有在“接近”十个段落中的另一个段落
包含另一个词。 假设第n段包含耶稣一词; 这个
如果段落之一,则该段落仅是结果的一部分
n-10,n-9,... n-1,n,n + 1,... n + 9,n + 10
包含上帝一词。 该查询是可交换的,因此查询:
神与十耶稣
返回相同的结果(如果有)。 请注意,“&”之间不允许有空格
符号和数字。 空格是可选的,因此上一个查询可以具有
被写为:
神与耶稣
如果单词包含前导数字,则必须在数字之间使用空格
第二个单词(如果它包含前导数字)。 如果号码不存在
假定值为0(零),即和查询的两个操作数都必须出现
仅在同一段落中。
最后一个查询语言元素是正则表达式。 正则表达式
查询中的格式如下:
= <delimeter> <正则表达式> <delimeter>
接下来的查询使用正则表达式,并且所有查询的结果相同:
= / esus /
= AesusA
=%esus%
前导和尾数必须相等,并且不能用于
正则表达式本身。 对于也阅读我的编译器的读者
文章系列,以下Backus Naur表示法描述了正式语法
查询语言:
查询=和查询{| 和查询} *
and-query =一元查询{&number? 一元查询} *
一元查询=(查询)| ! 一元查询| = delim正则表达式delim | 字
number =任何非负整数
regexp =任何正则表达式
delim =任何字符
单词=任意字母或数字序列
查询实现
查询子系统形成了一些类的层次结构。 你们都看过
接口已经存在(请参见上文)。 层次结构如下所示:
Query
|
+---- InternalQuery
|
+------- AbstractQuery
| |
| +------- QueryAnd
| |
| +------- QueryNot
| |
| +------- QueryOr
| |
| +------- QueryRex
|
+------- QueryTxt
QueryTxt类是查询编译器;
如您在开始时所见
本文的一部分,这是图书馆拥有的类
向用户分发查询。
所有其他具体类都从AbstractQuery类扩展而来,
记账的轨迹,例如makeBitSet()方法(参见上文)是
在此类中实现的方法。
其他类仅实现自己的“ evaluate()”方法。 编译时
成功的结果将是一个InternalQuery对象。 对象传递给
用户实际上是一个QueryTxt对象。 当用户调用任何方法时
在Query接口中定义的QueryTxt对象将调用委托给
内部查询对象,它是编译阶段的结果。
注意,简单的“单词”查询没有特殊的类。 这是处理
通过一个不需要“或”的QueryOr对象,只需为单个对象构建一个BitSet
查询中的单词。
文本
除QueryRex外,所有查询均不需要实际扫描实际
文字:他们都使用存储在库本身中的WordMap并构建
相应的BitSet。
就处理时间而言,QueryRex是一个“昂贵”的查询:它需要
从书签中检索实际文本并匹配正则表达式
反对这个文本。 使用的文本是BookMark的toString()的结果
方法。
此方法不仅返回段落文本,还返回组名,
书籍,章节和相关段落编号。 (请参阅上一篇文章
部分),因此QueryRex也可以搜索并找到这些名称。
结束语
下周,我将为您指向两个包含压缩文本的.zip文件
詹姆斯国王圣经和荷兰史泰登Vertaling圣经。 我会附上所有
带有下一部分文章的源代码。
您可以解压缩.zip文件,编译源代码,然后开始经营业务
然后:建立一个资料库并查询结果。 下周我会详细介绍
说明。
Prometheuzz不仅为我提供了荷兰史泰登Vertaling的全文,
圣经,但他还为我提供了两个.zip文件的磁盘空间。 我自己的ISP是
scrooge,我自己不能托管这两个文件,所以我非常感谢Prometheuzz
对于他的报价:非常感谢,我欠你一杯啤酒。
我希望下周见
亲切的问候,
乔斯
翻译自: https://bytes.com/topic/java/insights/691601-text-retrieval-systems-6-queries
文本检索 关键词检索和