Xapian 是一个开源搜索引擎库,使用 C++ 编写,并提供绑定(bindings )以允许从多种编程语言使用。它是一个高度适应性的工具包,允许开发人员轻松地将高级索引和搜索功能添加到自己的应用程序中。Xapian 支持多种加权模型和丰富的布尔查询运算符。最新稳定版本是 1.4.24,发布于 2023 年 11 月 6 日。

Xapian是20年前就开源的搜索引擎,整体比较稳定,功能层面较lucene有差距,但是足够成熟可用。唯一的缺憾是GPL V2协议。

安装

编译安装core

下载最新的tar包,解压并编译安装:

  1. tar xf xapian-core-1.4.24.tar.xz
  2. cd xapian-core-1.4.24/
  3. ./configure --prefix=/opt
  4. make
  5. make install

安装多语言绑定

需要先下载xapian-bindings-1.4.24,然后解压并编译:

  1. tar xf xapian-bindings-1.4.24.tar.xz
  2. cd xapian-bindings-1.4.24/
  3. ./configure XAPIAN_CONFIG=/data/xapian-core-1.4.24/xapian-config --with-java --with-python3
  4. make
  5. make install
  • configure 时,需要指定XAPIAN_CONFIG的路径,就是上面core里的路径
  • --with-java --with-python3 是只编译java 和 python3的绑定

使用

c++ 使用

可以在core目录,新建一个demo目录,新增src/main.cpp

  1. #include <iostream>
  2. #include <string>
  3. #include "xapian.h"
  4. const std::string index_data_path = "./index_data";
  5. const std::string doc_id1 = "doc1";
  6. const std::string doc_title1 = "如何 构建 搜索引擎 搜索 引擎";
  7. const std::string doc_content1 = "how to build search engine";
  8. const std::string doc_id2 = "doc2";
  9. const std::string doc_title2 = "搜索 是 一个 基本 技能";
  10. const std::string doc_content2 = "search is a basic skill";
  11. const int DOC_ID_FIELD = 101;
  12. void build_index()
  13. {
  14. std::cout << "--- build_index" << std::endl;
  15. Xapian::WritableDatabase db(index_data_path, Xapian::DB_CREATE_OR_OPEN);
  16. Xapian::TermGenerator indexer;
  17. Xapian::Document doc1;
  18. doc1.add_value(DOC_ID_FIELD, doc_id1); // custom property
  19. doc1.set_data(doc_content1); // payload
  20. indexer.set_document(doc1);
  21. indexer.index_text(doc_title1); // could use space seperated text line like terms or article
  22. db.add_document(doc1);
  23. Xapian::Document doc2;
  24. doc2.add_value(DOC_ID_FIELD, doc_id2); // custom property
  25. doc2.set_data(doc_content2);
  26. indexer.set_document(doc2);
  27. indexer.index_text(doc_title2);
  28. db.add_document(doc2);
  29. db.commit();
  30. }
  31. void search_op_or()
  32. {
  33. std::cout << "--- search_op_or" << std::endl;
  34. Xapian::Database db(index_data_path);
  35. Xapian::Enquire enquire(db);
  36. Xapian::QueryParser qp;
  37. // std::string query_str = "search engine";
  38. // Xapian::Query query = qp.parse_query(query_str);
  39. Xapian::Query term1("搜索");
  40. Xapian::Query term2("引擎");
  41. Xapian::Query query = Xapian::Query(Xapian::Query::OP_OR, term1, term2);
  42. std::cout << "query is: " << query.get_description() << std::endl;
  43. enquire.set_query(query);
  44. Xapian::MSet matches = enquire.get_mset(0, 10); // find top 10 results
  45. std::cout << matches.get_matches_estimated() << " results found" << std::endl;
  46. std::cout << "matches 1-" << matches.size() << std::endl;
  47. for (Xapian::MSetIterator it = matches.begin(); it != matches.end(); ++it)
  48. {
  49. Xapian::Document doc = it.get_document();
  50. std::string doc_id = doc.get_value(DOC_ID_FIELD);
  51. std::cout << "rank: " << it.get_rank() + 1 << ", weight: " << it.get_weight() << ", match_ratio: " << it.get_percent() << "%, match_no: " << *it << ", doc_id: " << doc_id << ", doc content: [" << doc.get_data() << "]\n" << std::endl;
  52. }
  53. }
  54. void search_op_and()
  55. {
  56. std::cout << "--- search_op_and" << std::endl;
  57. Xapian::Database db(index_data_path);
  58. Xapian::Enquire enquire(db);
  59. Xapian::QueryParser qp;
  60. Xapian::Query term1("搜索");
  61. Xapian::Query term2("技能");
  62. Xapian::Query query = Xapian::Query(Xapian::Query::OP_AND, term1, term2);
  63. std::cout << "query is: " << query.get_description() << std::endl;
  64. enquire.set_query(query);
  65. Xapian::MSet matches = enquire.get_mset(0, 10); // find top 10 results, like split page
  66. std::cout << matches.get_matches_estimated() << " results found" << std::endl;
  67. std::cout << "matches 1-" << matches.size() << std::endl;
  68. for (Xapian::MSetIterator it = matches.begin(); it != matches.end(); ++it)
  69. {
  70. Xapian::Document doc = it.get_document();
  71. std::string doc_id = doc.get_value(DOC_ID_FIELD);
  72. std::cout << "rank: " << it.get_rank() + 1 << ", weight: " << it.get_weight() << ", match_ratio: " << it.get_percent() << "%, match_no: " << *it << ", doc_id: " << doc_id << ", doc content: [" << doc.get_data() << "]\n" << std::endl;
  73. }
  74. }
  75. int main(int argc, char** argv)
  76. {
  77. std::cout << "hello xapian" << std::endl;
  78. build_index();
  79. search_op_or();
  80. search_op_and();
  81. return 0;
  82. }

cmake 文件

  1. cmake_minimum_required(VERSION 3.24)
  2. project(xapian_demo)
  3. set(CMAKE_CXX_STANDARD 11)
  4. set(CMAKE_CXX_STANDARD_REQUIRED ON)
  5. include_directories(
  6. ../include
  7. )
  8. link_directories(
  9. ../.libs
  10. )
  11. file(GLOB SRC
  12. src/*.h
  13. src/*.cpp
  14. )
  15. add_executable(${PROJECT_NAME} ${SRC})
  16. target_link_libraries(${PROJECT_NAME}
  17. xapian uuid
  18. )

编译、测试:

  1. #cmake .
  2. -- Configuring done
  3. -- Generating done
  4. -- Build files have been written to: /data/xapian-core-1.4.24/demo
  5. #make
  6. Consolidate compiler generated dependencies of target xapian_demo
  7. [ 50%] Building CXX object CMakeFiles/xapian_demo.dir/src/main.cpp.o
  8. [100%] Linking CXX executable xapian_demo
  9. [100%] Built target xapian_demo
  10. #./xapian_demo
  11. hello xapian
  12. --- build_index
  13. --- search_op_or
  14. query is: Query((搜索 OR 引擎))
  15. 2 results found
  16. matches 1-2
  17. rank: 1, weight: 0.500775, match_ratio: 100%, match_no: 1, doc_id: doc1, doc content: [how to build search engine]
  18. rank: 2, weight: 0.0953102, match_ratio: 19%, match_no: 2, doc_id: doc2, doc content: [search is a basic skill]
  19. --- search_op_and
  20. query is: Query((搜索 AND 技能))
  21. 1 results found
  22. matches 1-1
  23. rank: 1, weight: 0.500775, match_ratio: 100%, match_no: 2, doc_id: doc2, doc content: [search is a basic skill]

python 使用

上面c++的测试仅有几条数据,python我们来上点压力。

搜索数据源是包含上百万数据的xml,文件里数据格式是给manticore使用的sphinxxml格式:

  1. <sphinx:document id="3669513577616591688"><domain_rank><![CDATA[0]]></domain_rank><page_rank><![CDATA[0]]></page_rank><author_rank><![CDATA[0]]></author_rank><update_ts><![CDATA[1671120000000]]></update_ts><crawl_ts><![CDATA[1702765056760]]></crawl_ts><index_ts><![CDATA[1703141806692]]></index_ts><freq><![CDATA[0]]></freq><pv><![CDATA[0]]></pv><comment><![CDATA[0]]></comment><forward><![CDATA[0]]></forward><up><![CDATA[0]]></up><title_lac><![CDATA[南充市 首席 风水 大师 罗 李华 百科 词典]]></title_lac><title_jieba><![CDATA[南充市 首席 风水 大师 罗李华 百科词典]]></title_jieba><summary_lac><![CDATA[百科 词典 , 主要 收录 知名 人物 、 企业 、 行业 相关 词条 为主 , 是 由 各 大网民 申请 供稿 , 由 专职 人员 严格 审核 编辑 而成 , 力求 做到 每一个 词条 权威 、 真实 、 客观 、 专业 , 旨在 打造 一个 值得 大家 信赖 的 权威 百科 平台 。]]></summary_lac><summary_jieba><![CDATA[百科词典 , 主要 收录 知名 人物 、 企业 、 行业 相关 词条 为主 , 是 由 各大 网民 申请 供稿 , 由 专职人员 严格 审核 编辑 而成 , 力求 做到 每 一个 词条 权威 、 真实 、 客观 、 专业 , 旨在 打造 一个 值得 大家 信赖 的 权威 百科 平台 。]]></summary_jieba><url><![CDATA[https://www.baikecidian.cn/h-nd-9709.html]]></url><domain><![CDATA[www.baikecidian.cn]]></domain><keywords_lac><![CDATA[]]></keywords_lac><image_link><![CDATA[0]]></image_link><post_ts><![CDATA[1538215160000]]></post_ts></sphinx:document>

因此,我们先编写一个读取程序:

  1. import xmltodict
  2. def read_sphinx_xml(file_path):
  3. file = open(file_path, 'r', encoding='utf-8')
  4. xml_str = ''
  5. end_tag = '</sphinx:document>'
  6. for line in file:
  7. if end_tag in line:
  8. try:
  9. xml_str = xml_str + line
  10. xml_dict = xmltodict.parse(xml_str)
  11. yield xml_dict['sphinx:document']
  12. except Exception as e:
  13. print(xml_str)
  14. print(e)
  15. xml_str = ''
  16. else:
  17. xml_str = xml_str + line

然后,调用xapian的binding接口来构建索引:

  1. def list_files(path):
  2. return [item for item in os.listdir(path) if ".txt" in item]
  3. DOC_ID_FIELD = 101
  4. DOC_TITLE_FIELD = 102
  5. ### Start of example code.
  6. def index(datapath, dbpath):
  7. # Create or open the database we're going to be writing to.
  8. db = xapian.WritableDatabase(dbpath, xapian.DB_CREATE_OR_OPEN)
  9. termgenerator = xapian.TermGenerator()
  10. count = 0
  11. for file in list_files("/data"):
  12. print(f'start load data from {file}')
  13. for fields in read_sphinx_xml(f"/data/{file}"):
  14. title = fields.get('title_jieba', '')
  15. summary = fields.get('summary_jieba', '')
  16. identifier = fields.get('@id', '')
  17. if summary is None:
  18. summary = ''
  19. if title is None:
  20. continue
  21. count = count + 1
  22. doc = xapian.Document()
  23. termgenerator.set_document(doc)
  24. # title 放大5倍
  25. termgenerator.index_text(title * 5 + ' ' + summary)
  26. # 存入数据
  27. doc.add_value(DOC_ID_FIELD, identifier)
  28. doc.add_value(DOC_TITLE_FIELD, title)
  29. doc.set_data(identifier + ' ' + title)
  30. # indexer.
  31. idterm = u"Q" + identifier
  32. doc.add_boolean_term(idterm)
  33. db.replace_document(idterm, doc)
  34. if count % 10000 == 0:
  35. print(f'loaded {count}')

注意:

  • xapian对字段支持的不够好,需要用suffix实现,这里测试就将title放大5倍混合summary进行建立索引
  • doc.add_value 可以存储字段值,后续可以doc.get_value读取
  • doc.set_data 可以用来存储doc的完整信息,方便显示,doc信息会存储在独立的doc文件中
  • 这里add_boolean_term和replace_document,可以实现相同id的数据覆盖

下面来看查询

  1. #!/usr/bin/env python
  2. import json
  3. import sys
  4. import xapian
  5. import support
  6. import time
  7. def search(dbpath, querystring, offset=0, pagesize=10):
  8. # offset - defines starting point within result set
  9. # pagesize - defines number of records to retrieve
  10. # Open the database we're going to search.
  11. db = xapian.Database(dbpath)
  12. # Set up a QueryParser with a stemmer and suitable prefixes
  13. queryparser = xapian.QueryParser()
  14. query = queryparser.parse_query(querystring)
  15. print(query)
  16. # Use an Enquire object on the database to run the query
  17. enquire = xapian.Enquire(db)
  18. enquire.set_query(query)
  19. start_time = time.time()
  20. # And print out something about each match
  21. matches = []
  22. for match in enquire.get_mset(offset, pagesize):
  23. print(f'rank: {match.rank} weight: {match.weight} docid: {match.document.get_value(101).decode("utf-8")} title: {match.document.get_value(102).decode("utf-8")}')
  24. # print(match.document.get_data().decode('utf8'))
  25. matches.append(match.docid)
  26. print(f'cost time {1000 * (time.time() - start_time)}ms')
  27. # Finally, make sure we log the query and displayed results
  28. support.log_matches(querystring, offset, pagesize, matches)
  29. if len(sys.argv) < 3:
  30. print("Usage: %s DBPATH QUERYTERM..." % sys.argv[0])
  31. sys.exit(1)
  32. search(dbpath = sys.argv[1], querystring = " ".join(sys.argv[2:]))

解释:

  • xapian.QueryParser() 可以解析查询query,可以使用+ -,默认是or`查询
  • 依然通过xapian.Enquire对象查询,通过get_mset获取结果
  • doc可以通过document.get_value读取存储的字段值,可以通过get_data读取存储的doc信息,要显示需要先decode('utf8')

下面来测试查询,在已构建的330万+索引数据上,搜索 21 世纪 十大 奇迹 都 有 哪些

默认的or查询,耗时46ms:

  1. (base) [root@dev demo]#python3 py_search.py ./test_index_2/ '21 世纪 十大 奇迹 都 有 哪些'
  2. Query((21@1 OR 世纪@2 OR 十大@3 OR 奇迹@4 OR 都@5 OR 有@6 OR 哪些@7))
  3. rank: 0 weight: 36.96501079176272 docid: 270926605591973127 title: 21 世纪 十大 奇迹 ( 王金宝 )
  4. rank: 1 weight: 26.66735387825444 docid: 1202595084889677840 title: 淮安 十大 装修 公司 排行榜 哪些
  5. rank: 2 weight: 26.637435058757113 docid: 4515279401098254828 title: 十大 轻奢 首饰 品牌 耳环 ( 十大 轻奢 首饰 品牌 耳环 排名 )
  6. rank: 3 weight: 25.896035383457647 docid: 2734857435606641662 title: 中国 十大 奇迹 什么
  7. rank: 4 weight: 25.705459264178575 docid: 7786914994161493217 title: 每个 民族 伤痕 血泪 ( ) , 再说 曾经 创造 奇迹 蒙古 帝国 !
  8. rank: 5 weight: 25.5095343276925 docid: 1500823194476917788 title: 真正 复古 奇迹 手游安卓 下载 2022 十大 真正 复古 奇迹 手游 推荐 ...
  9. rank: 6 weight: 25.47914915723924 docid: 868651613852701914 title: 21 世纪 哪些 著名 科学家 哪些 ? ?
  10. rank: 7 weight: 25.41860730241055 docid: 7128947999947583631 title: 西安 临潼区 必玩 十大 景区 , 西安 临潼区 哪些 景点 推荐 旅游 ...
  11. rank: 8 weight: 25.16026635261191 docid: 6074515952166234396 title: 世界 建筑史 堪称 逆天 十大 工程 , 个个 奇迹 !
  12. rank: 9 weight: 24.89609264689645 docid: 5578567283356182005 title: 20 世纪 科技 发明 哪些 20 世纪 哪些 重大 科学 发现 科学 ...
  13. cost time 46.19002342224121ms
  14. '21 世纪 十大 奇迹 都 有 哪些'[0:10] = 461487 2291460 457410 1416736 3245773 1156355 3030607 2498966 2025338 254698

如何优化查询耗时呢,我们可以先预测,这里 十大 奇迹 是核心词,我们可以要求必出,因此查询串可以变为: 21 世纪 +十大 +奇迹 都 有 哪些

  1. (base) [root@dev demo]#python3 py_search.py ./test_index_2/ '21 世纪 +十大 +奇迹 都 有 哪些'
  2. Query(((十大@3 AND 奇迹@4) AND_MAYBE (21@1 OR 世纪@2 OR (都@5 OR 有@6 OR 哪些@7))))
  3. rank: 0 weight: 36.96293887882541 docid: 270926605591973127 title: 21 世纪 十大 奇迹 ( 王金宝 )
  4. rank: 1 weight: 25.89233097995836 docid: 2734857435606641662 title: 中国 十大 奇迹 什么
  5. rank: 2 weight: 25.505700206213298 docid: 1500823194476917788 title: 真正 复古 奇迹 手游安卓 下载 2022 十大 真正 复古 奇迹 手游 推荐 ...
  6. rank: 3 weight: 25.41629259671702 docid: 7128947999947583631 title: 西安 临潼区 必玩 十大 景区 , 西安 临潼区 哪些 景点 推荐 旅游 ...
  7. rank: 4 weight: 25.156904086936752 docid: 6074515952166234396 title: 世界 建筑史 堪称 逆天 十大 工程 , 个个 奇迹 !
  8. rank: 5 weight: 24.62510506307912 docid: 193253728534326320 title: 十大 凶梦有 哪些 ? 十大 凶梦 列表 ! 观音 灵签 算命网
  9. rank: 6 weight: 23.192754028779266 docid: 7179285817750982899 title: 十大 电脑 恐怖 游戏 排行 好玩 恐怖 游戏 哪些
  10. rank: 7 weight: 23.14557703440898 docid: 8499116988738957144 title: 十大 爆火 奇迹 类手游 排行榜 最火 奇迹 类手游 排名 前十 ...
  11. rank: 8 weight: 22.274870321417836 docid: 1134007698166133600 title: 世界 十大 著名 建筑物 感受 人类 辉煌 奇迹 建筑 第一 排行榜
  12. rank: 9 weight: 22.214192030795594 docid: 7678030174605825797 title: 世界 十大 奇迹 动物 : 爱尔兰 大鹿 死而复生 世界 十大 建筑 奇迹
  13. cost time 2.651214599609375ms
  14. '21 世纪 +十大 +奇迹 都 有 哪些'[0:10] = 461487 1416736 1156355 2498966 2025338 173861 448901 723659 1029533 1830781

耗时3ms不到,且结果更优质。

总结

xapian的介绍到这里告一段落,后续文章会深入xapian的内部细节。

xapian 搜索引擎介绍与使用入门的更多相关文章

  1. mxgraph进阶(二)mxgraph的初步介绍与开发入门

    mxgraph的初步介绍与开发入门 前言 由于小论文实验需求,需要实现根据用户日志提取出行为序列,然后根据行为序列生成有向图的形式,并且连接相邻动作的弧上标有执行此次相邻动作的频次.为此,在大师兄徐凯 ...

  2. 第三百五十九节,Python分布式爬虫打造搜索引擎Scrapy精讲—elasticsearch(搜索引擎)介绍以及安装

    第三百五十九节,Python分布式爬虫打造搜索引擎Scrapy精讲—elasticsearch(搜索引擎)介绍以及安装 elasticsearch(搜索引擎)介绍 ElasticSearch是一个基于 ...

  3. 三十八 Python分布式爬虫打造搜索引擎Scrapy精讲—elasticsearch(搜索引擎)介绍以及安装

    elasticsearch(搜索引擎)介绍 ElasticSearch是一个基于Lucene的搜索服务器.它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口.Elasticse ...

  4. _00017 Kafka的体系结构介绍以及Kafka入门案例(0基础案例+Java API的使用)

    博文作者:妳那伊抹微笑 itdog8 地址链接 : http://www.itdog8.com(个人链接) 博客地址:http://blog.csdn.net/u012185296 博文标题:_000 ...

  5. Web安全之Web 安全介绍与基础入门知识

    web安全介绍与基础入门知识 安全与安全圈 甲方与乙方 甲方:如腾讯,阿里等需要安全服务的公司 乙方:提供安全服务产品的服务型安全公司 web与二进制 web,研究web安全 二进制,研究如客户端安全 ...

  6. MyBatis - 介绍、简单入门程序

    JDBC编程中的问题     1. 将SQL语句硬编码到Java代码,不利于系统维护.         设想如何解决:将SQL单独抽取出来,在配置文件(xml方式.properties文件)进行配置. ...

  7. Vue+koa2开发一款全栈小程序(1.课程介绍+2.ES6入门)

    1.课程介绍 1.课程概述 1.做什么? Vue+koa2开发一款全栈小程序 2.哪些功能? 个人中心.图书列表.图书详情.图书评论.个人评论列表 3.技术栈 小程序.Vue.js.koa2.koa- ...

  8. Lucene介绍及简单入门案例(集成ik分词器)

    介绍 Lucene是apache软件基金会4 jakarta项目组的一个子项目,是一个开放源代码的全文检索引擎工具包,但它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和 ...

  9. Shodan搜索引擎介绍

    from:http://www.exploit-db.com/wp-content/themes/exploit/docs/33859.pdf 0x00 概要 这篇文章可以作为渗透测试人员和安全工作者 ...

  10. XAPIAN简单介绍(三)

    今天主要介绍的是Xapian::Database这个类.先上图 看上去就非常恐怖的吧,我们一点点的说. 首先一切的開始都来自那个include目录中的database.h,他的直接实现是在omdata ...

随机推荐

  1. Opencv实例练习

    实例所用的函数可在另一篇文章查询:  https://www.cnblogs.com/Zhouce/p/17867164.html 1.图像读取 1 import cv2 # 引入opencv库 2 ...

  2. 上传代码到github和删除.git

    在本地建立目录,将想上传的代码放到该文件夹中 git init ###初始化 git add . ###将本地项目工作区的所有文件添加到暂存区 git commit -m "excu&quo ...

  3. MacOS Monterey 配置 PHP 环境记录

    目前 中文网中对于 MacOS 下安装 PHP 教程比较老,并且我个人感觉很难看懂.我在安装 PHP 过程中遇到了很多网络中没有出现过的问题,特此环境配置过程记录如下. 电脑:MacBook Pro ...

  4. Java项目整合短信验证码

    一.开通短信服务 本来想整合阿里云短信服务的,可是签名一直审核不过,所以在阿里云的云市场找到了一个替代产品(sddx) 接下来小伙伴们按照自己的经济实力购买或者用免费的5条(我就是用免费的5条了) 购 ...

  5. 最新消息:OpenAI GPT Store 正式上线,GPTs 应用商店来了!

    OpenAI推出的两款新产品和服务:GPT Store和ChatGPT Team,提供了许多全新的解决方案和功能,旨在帮助用户更轻松地使用和构建GPT工具,同时也增加了公司的收入来源.GPT Stor ...

  6. 2023-09-10:用go语言编写。作为项目经理,你规划了一份需求的技能清单 req_skills, 并打算从备选人员名单 people 中选出些人组成一个「必要团队」 ( 编号为 i 的备选人员

    2023-09-10:用go语言编写.作为项目经理,你规划了一份需求的技能清单 req_skills, 并打算从备选人员名单 people 中选出些人组成一个「必要团队」 ( 编号为 i 的备选人员 ...

  7. 通过鼠标拖拉获取图片原像素的两个点坐标vue

    <template> <div> <img class="no-drag" ref="image" src="https ...

  8. 数仓实践丨主动预防-DWS关键工具安装确认

    摘要:gdb确认是否安装,所带来的该工具用户数据库实例触发core问题后集群状态反复异常,对此问题及时分析根因并及时进行规避. 本文分享自华为云社区<主动预防-DWS关键工具安装确认>,作 ...

  9. 自动调优工具AOE,让你的模型在昇腾平台上高效运行

    摘要:当算子性能或者网络性能不佳时,可以使用AOE进行调优.本文就带大家了解自动调优工具AOE,让你的模型在昇腾平台上高效运行. 本文分享自华为云社区<自动调优工具AOE,让你的模型在昇腾平台上 ...

  10. 划重点!DWS开发的五大要点

    摘要:高效使用数据库是一个合格的开发工程师的必备技能,如何使用DWS进行高效开发,提升应用效率,技术干货来喽~~~ 高效使用数据库是一个合格的开发工程师的必备技能,如何使用DWS进行高效开发,提升应用 ...