我有一个反向索引(作为字典),我想把一个布尔搜索查询作为输入来处理它并产生一个结果。
倒排索引是这样的:
{
Test : { FileName1: [213, 1889, 27564], FileName2: [133, 9992866, 27272781, 78676818], FileName3: [9211] },
Try : { FileName4 ...
.....
}
现在,给定一个布尔搜索查询,我必须返回结果。
示例:
布尔搜索查询:test AND try
结果应该是所有包含单词test和try的文档。
布尔搜索查询:test OR try
结果应该是所有测试或尝试的文档。
布尔搜索查询:test AND NOT try
结果应该是所有已经测试但未尝试的文档。
如何构建此搜索引擎以处理给定的布尔搜索查询?
提前致谢!
答案 0 :(得分:3)
另一种方法可能是张贴列表的内存内交集(对于AND案例,您可以针对OR,NOT等进行增强)。
在发布列表上附加了一个简单的合并算法,假设列表已排序(增加doc_id顺序,如果我们为索引建立索引,则可以轻松实现)正确的术语)-这将改善时间复杂度(O(n1 + n2)),因为我们将对已排序列表执行线性合并,并且可能会更早停止。
现在假定我们的位置倒排索引看起来像这样 :(与您的位置类似,但将列表作为列表而不是字典发布-这将允许将来使用时进行压缩) 映射的位置-字符串>术语,而每个术语由(tf,发布列表([P1,P2,...]))组成,每个发布都有(docid,df,位置列表)。现在,我们可以迭代地对所有发布列表执行简单的AND:
def search(self, sq: BoolQuery) -> list:
# Performs a search from a given query in boolean retrieval model,
# Supports AND queries only and returns sorted document ID's as result:
if sq.is_empty():
return super().search(sq)
terms = [self.index[term] for term in sq.get_terms() if term in self.index]
if not terms:
return []
# Iterate over posting lists and intersect:
result, terms = terms[0].pst_list, terms[1:]
while terms and result:
result = self.intersect(result, terms[0].pst_list)
terms = terms[1:]
return [p.id for p in result]
现在让我们看一下交叉点:
def intersect(p1: list, p2: list) -> list:
# Performs linear merge of 2x sorted lists of postings,
# Returns the intersection between them (== matched documents):
res, i, j = list(), 0, 0
while i < len(p1) and j < len(p2):
if p1[i].id == p2[j].id:
res.append(p1[i])
i, j = i + 1, j + 1
elif p1[i].id < p2[j].id:
i += 1
else:
j += 1
return res
以后可以在执行词组搜索时扩展此简单算法(编辑交点以计算倾斜距离,例如:| pos1-pos2 | <倾斜)
答案 1 :(得分:2)
您似乎想要做的是创建一个查询字符串解析器,它将读取查询字符串并将其转换为一系列AND / OR / NOT组合以返回正确的键。
有两种方法。
SQL表至少包含:
CREATE TABLE my_data(
dictkey text,
data text);
python_query="foo OR bar AND NOT gazonk"
sql_keywords=["AND","NOT","OR"]
sql_query=[]
for word in python_query.split(" "):
if word in sql_keywords:
sql_query+=[ word ]
else:
sql_query+=["dictkey='%s'" % word]
real_sql_query=" ".join(sql_query)
这需要对SQL注入和特殊字符进行一些转义和控制检查,但一般情况下它只会将您的查询转换为SQL,当针对SQL数据库运行时,它将返回密钥(可能还有数据)以供进一步处理。
您需要做的是分析您获得的字符串并将逻辑应用于现有的Python数据。
分析字符串以将其减少为特定组件(及其交互)是parsing。如果你真的想要构建自己的完全成熟的解析器,那么就会有Python模块,但是,对于学校作业,我希望你的任务是建立自己的。
根据您的说明,查询可以用准BNF form表示为:
(<[NOT] word> <AND|OR>)...
既然你说优先级与所有优先级无关,你可以通过简单的方式进行,并逐字解析。
然后你必须将关键字与文件名相匹配,正如另一个答案中所提到的,这对于sets来说是最容易的。
所以,它可能会像这样:
import re
query="foo OR bar AND NOT gazonk"
result_set=set()
operation=None
for word in re.split(" +(AND|OR) +",query):
#word will be in ['foo', 'OR', 'bar', 'AND', 'NOT gazonk']
inverted=False # for "NOT word" operations
if word in ['AND','OR']:
operation=word
continue
if word.find('NOT ') == 0:
if operation is 'OR':
# generally "OR NOT" operation does not make sense, but if it does in your case, you
# should update this if() accordingly
continue
inverted=True
# the word is inverted!
realword=word[4:]
else:
realword=word
if operation is not None:
# now we need to match the key and the filenames it contains:
current_set=set(inverted_index[realword].keys())
if operation is 'AND':
if inverted is True:
result_set -= current_set
else:
result_set &= current_set
elif operation is 'OR':
result_set |= current_set
operation=None
print result_set
请注意,这不是一个完整的解决方案(例如,它不包括处理查询的第一个术语,它要求布尔运算符为大写),并且未经过测试。但是,它应该主要用于向您展示如何进行操作。做更多就是为你写课程,这对你有害。因为您需要学习如何操作,以便您能够理解它。随意请求澄清。
答案 2 :(得分:0)
考虑到你有倒排索引,这是一个以test
和try
为键的字典,你可以定义以下函数并使用它们:
def intersection(list1, list2):
return list(set(list1).intersection(list2))
def union(list1, list2):
return list(set(list1).union(list2))
def notin(list1, list2)
return [filter(lambda x: x not in list1, sublist) for sublist in list2]
intersection(inverted_index['people'].keys(), intersection(inverted_index['test'].keys(), inverted_index['try'].keys()))