DocSearcher:文档搜索引擎

这篇具有很好参考价值的文章主要介绍了DocSearcher:文档搜索引擎。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

项目描述

我们要实现什么样的搜索引擎呢?
该项目主要针对Java API 文档设计出一款文档搜索引擎,当用户在页面上输入查询词后,能够快速的匹配出相关API在线文档,补充了Java在线文档中没有搜索的功能。 每个搜索结果中包含了标题, 描述, 展示 url 和点击 url等信息,便于用户浏览。
Java API 文档线上版本参见: https://docs.oracle.com/javase/8/docs/api/index.html

项目流程

1.对离线版本的HTML文档进行解析,将解析的结果整理为一个行文本文件;
2.读取处理好的行文本文件进行Ansj分词,权重计算等操作,在内存中构造正排索引和倒排索引;
3.根据输入查询词进行分词、触发依据相似性分析以及倒排索引结构对结果进行检索排序,并以Json格式进行包装后序列化为字符串返回;
4.编写简单页面,通过HTTP服务器搭载搜索页面,点击搜索结果可跳转至对应API在线文档。
开发环境:IDEA、Tomcat、Maven、JDK1.8
相关技术栈:Ansj分词、倒排索引、过滤器、HTML、Servlet、Json、Ajax
项目代码:https://github.com/xiaoting-hub/Projects-DocSearcher

基础知识

什么是倒排索引?

文档(DOC):经过预处理,用户输入关键字要被检索的页面
正排索引:“一个文档包含了哪些词”。描述一个文档的基本信息, 包括文档标题、 正文,以及文档标题和正文的分词/断句结果。
倒排索引:“一个词被哪些文档引用了”。 描述一个词的基本信息, 包括这个词都被哪些文档引用, 这个词在该文档中的重要程度以及这个词的出现位置等信息。

问题:为什么要用倒排索引?暴力搜索行不行啊?
每次处理搜索请求的时候, 拿着查询词去所有的网页中搜索一遍,检查每个网页是否包含查询词字符串。显然,暴力搜索这种方式随着文档数量的增加开销会线性增加,一般我们对搜索引擎的效率还是比较看重的。所以尽可能的高效才是重点(^ . ^)。

为什么要进行分词?分词的原理,在该项目中分词如何来实现?

用户输入的关键字有时候是多个词/一句话,要搜索准确就必须进行分词,分词原理有两个方面:一种是基于词库,尝试把这些词进行穷举,放到字典文件中,我们可以依次取句子中的内容,每隔一个词进行查找。第二种是基于统计,会有很多官方的语料库进行人工标注/统计,分词技术在NLP中也比较常见。我们在该项目中使用Maven中的第三方库Ansj分词技术。
附带依赖链接:https://mvnrepository.com/artifact/org.ansj/ansj_seg/5.1.6

基本实现

模块划分

项目总共划分为三个模块:
索引模块:扫描下载到的文档,分析数据内容构建正排+倒排索引,并保存到文件中。
搜索模块:加载索引。根据输入的查询词, 基于正排+倒排索引进行检索,得到检索结果。
Web模块:编写一个简单的页面,展示搜索结果。点击其中的搜索结果能跳转到对应的 Java API 文档页面。

创建项目

使用IDEA创建一个SpringBoot项目,具体细节不在详细赘述。项目的目录结结构大致是这样:
DocSearcher:文档搜索引擎

引入分词依赖

使用Ansj分词第三方库,可以看一些简单的示例:java分词-ansj的初次使用
在pom.xml中注入依赖:

<dependency>
    <groupId>org.ansj</groupId>
   	<artifactId>ansj_seg</artifactId>
    <version>5.1.6</version>
</dependency>

注意:当 ansj 对英文分词时,会自动把单词大写转为小写。

实现索引模块

我们要实现的是在本地基于离线文档制作索引,实现检索,当用户在搜素结果页点击具体的搜索结果时,就自动跳转到在线文档的页面。跳转过去的目标页面也称为落地页面。
DocSearcher:文档搜索引擎

1. 实现Parse类

Parse类构建一个可执行程序。
① 根据指定路径,枚举出该路径中的所有文件(html),这个过程中需要把所有子目录的文件获取到;
② 根据文件罗列出的文件路径,打开文件,读取文件内容,解析并构建索引;
a) 标题:直接使用解析操作
b) URL:基于文件路径进行了简单拼接(离线文档和线上文档路径的关系)
c) 正文:核心操作,去标签~简单粗暴的方式实现的。使用<>作为“是否考虑要拷贝数据”的开关
③ 把在文件中构建好的索引数据结构,保存在指定的文件,使用Index类中的addDoc()方法。
Parse类最主要的事情是辅助Index类完成索引的制作过程。详细代码见上述github链接。

public class Parser {
   
    private static final String INPUT_PATH = "E:/IdeaProjects/doc_searcher_index/jdk-8u231-docs-all/docs/api";
    //TODO补充上索引实例
    public static void main(String[] args) throws InterruptedException {
   
        Parser parser = new Parser();
        parser.run();
    }
    
    public void run() {
   
        System.out.println("开始解析!");
        long beg = System.currentTimeMillis();
        // 1. 枚举出这个目录下的所有文件
        ArrayList<File> fileList = new ArrayList<>();
        enumFile(INPUT_PATH, fileList);
        for (File f : fileList) {
   
            System.out.println("解析 " + f.getAbsolutePath());
            // 2. 针对每个文件, 打开, 并读取内容, 进行转换
            parseHTML(f);
        }
        System.out.println("解析完成! 开始保存索引!");
        long end = System.currentTimeMillis();
        System.out.println("保存索引完成! 时间: " + (end - beg));
    }

    // 递归完成目录枚举过程
    private void enumFile(String rootPath, ArrayList<File> fileList) {
   
        File rootFile = new File(rootPath);
        File[] files = rootFile.listFiles();
        for (File f : files) {
   
            if (f.isDirectory()) {
   
                enumFile(f.getAbsolutePath(), fileList);
            } else if (f.getAbsolutePath().endsWith(".html")) {
   
                fileList.add(f);
            }
        }
    }
    
    private void parseHTML(File f) {
   
        // 1. 转换出标题
        String title = parseTitle(f);  //得到的文件名字-“.html”
        // 2. 转换出 url
        String url = parseUrl(f);  //网络URL和本地URL进行拼接
        // 3. 转换出正文(正文需要去除 html 标签)
        String content = parseContent(f);  // 先按照一个字符一个字符的方式来读取,以 < 和 > 来控制拷贝数据的开关
        // 4. TODO 添加到索引中
        
    }

    private String parseTitle(File f) {
   
        // 直接使用文件名作为标题
        String name = f.getName();
        return name.substring(0, name.length() - ".html".length());
    }

    private String parseUrl(File f) {
   
        // 这个 url 是指在线文档对应的链接.
        // url 由两个部分构成.
        // 第一部分是 https://docs.oracle.com/javase/8/docs/api
        // 第二部分是 文件路径中 api 之后的部分.
        String part1 = "https://docs.oracle.com/javase/8/docs/api";
        String part2 = f.getAbsolutePath().substring(INPUT_PATH.length());
        return part1 + part2;
    }

    private String parseContent(File f) {
   
        // 读取文件内容, 并去除其中的 html 标签和换行
        try {
   
            FileReader fileReader = new FileReader(f);
            // 是否当前读的字符是正文
            boolean isContent = true;
            StringBuilder output = new StringBuilder();
            while (true) {
   
                int ret = fileReader.read();
                if (ret == -1) {
   
                    break;
                }
                char c = (char)ret;
                if (isContent) {
   
                    if (c == '<') {
   
                        isContent = false;
                        continue;
                    }
                    if (c == '\n' || c == '\r') {
   
                        c = ' ';
                    }
                    output.append(c);
                } else {
   
                    if (c == '>') {
   
                        isContent = true;
                    }
                }
            }
            fileReader.close();
            return output.toString();
        } catch (IOException e) {
   
            e.printStackTrace();
        }
        return "";
    }
}

DocSearcher:文档搜索引擎

2. 实现Index

Index 负责构建索引数据结构。主要提供以下方法:

  • getDocInfo():根据 docId 查正排,返回类型是DocInfo类,包含文档中的详细信息(docId, title, url, content),直接按照下标来取元素
  • getInverted():根据关键词查倒排,返回值类型为List列表;Weight包含了docId和weight权重;按照key取HashMap<String, ArrayList>的value即可
  • addDoc(): 往索引中新增一个文档,包括构建正排索引和构建倒排索引。①构建正排,构造DocInfo对象,添加到正排索引末尾。②构建倒排,先进行标题和正文分词,统计词频。遍历分词结果,去更新倒排索引中对应的倒排拉链即可,同时注意线程安全问题。
  • save():往磁盘中写索引数据,使用ObjectMapper类保存成Json格式;ObjectMapper类是Jackson库的主要类。它能够提供writeValue()和readValue()方法将Java对象和JSON结构相互转换(序列化和反序列化),基于JSON格式把索引数据保存到指定文件中
  • load():从磁盘加载索引数据,基于JSON格式对数据进行解析,将硬盘中的文件读出来,解析到内存中
创建Index类

首先,我们要了解Parse类和Index类的关系,Parse类相当于制作索引的入口,Index类相当于实现了索引的数据结构,可以提供一些API。
所用到的类结构:
DocSearcher:文档搜索引擎

public class Index {
   
    public static final String INDEX_PATH = "E:/IdeaProjects/doc_searcher_index/";
    // 正排索引, 下标对应 docId
    private ArrayList<DocInfo> forwardIndex = new ArrayList<>();
    // 倒排索引, key 是分词结果, value 是这个分词 term 对应的倒排拉链(包含一堆 docid)
    private HashMap<String, ArrayList<Weight>> invertedIndex = new HashMap<>();

    // 根据 docId 查正排
    public DocInfo getDocInfo(int docId) {
   }
    // 根据 分词结果 查倒排
    public ArrayList<Weight> getInverted(String term) {
   }
    // 向索引中新增一条文档
    public void addDoc(String title, String url, String content) {
   }
    // 加载索引文件
    public void load() {
   }
    // 保存索引文件
    public void save() {
   }
}
创建Weight类

Weight类表示一个文档的权重信息,其中 weight 的值通过词出现的频率来构造。
在代码中使用的是:weight = 10×标题中出现的次数 + 1×正文中出现的次数。文章来源地址https://www.toymoban.com/news/detail-414856.html

class Weight {
   
    private int docId;
    private int weight;
    public int getDocId() {
   return docId;}
    public void setDocId(int docId) {
   this.docId = docId;}
    public int getWeight() {
   return weight;}
    public void setWeight(int weight) {
   this.weight = weight;}
}
实现 getDocInfo 和 getInverted
// 根据 docId 查正排
public DocInfo getDocInfo(int docId) {
   
    return forwardIndex.get(docId);
}

// 根据 分词结果 查倒排
public ArrayList<Weight> getInverted(String term) {
   
    return invertedIndex.get(term);
}
实现addDoc
DocInfo docInfo = buildForward

到了这里,关于DocSearcher:文档搜索引擎的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [C++项目] Boost文档 站内搜索引擎(5): cpphttplib实现网络服务、html页面实现、服务器部署...

    在前四篇文章中, 我们实现了从文档文件的清理 到 搜索的所有内容: 项目背景: 🫦[C++项目] Boost文档 站内搜索引擎(1): 项目背景介绍、相关技术栈、相关概念介绍… 文档解析、处理模块 parser 的实现: 🫦[C++项目] Boost文档 站内搜索引擎(2): 文档文本解析模块parser的实现、如何对

    2024年02月13日
    浏览(45)
  • [C++项目] Boost文档 站内搜索引擎(4): 搜索的相关接口的实现、线程安全的单例index接口、cppjieba分词库的使用、综合调试...

    有关 Boost 文档搜索引擎的项目的前三篇文章, 已经分别介绍分析了: 项目背景: 🫦[C++项目] Boost文档 站内搜索引擎(1): 项目背景介绍、相关技术栈、相关概念介绍… 文档解析、处理模块 parser 的实现: 🫦[C++项目] Boost文档 站内搜索引擎(2): 文档文本解析模块parser的实现、如何对

    2024年02月14日
    浏览(49)
  • API 文档搜索引擎

    在搜狗搜索的搜索结果页中, 包含了若干条结果, 每一个结果包含了图标, 标题, 描述, 展示URL等 搜索引擎的本质: 输入一个查询词, 得到若干个搜索结果, 每个搜索结果包含了标题, 描述, 展示URL和点击URL 2.1 搜索的核心思路: 当前我们有很多的网页(假设上亿个), 每个网页我们称

    2024年02月13日
    浏览(36)
  • Java文档搜索引擎

    像百度,搜狗这种搜索一个,然后在结果页中展示若干条结果,每一个结果中, 又包含了图标, 标题, 描述, 展示url, 时间, 子链, 图片等;当然像这种搜索引擎属于全站搜索,这个项目是要实现站内搜索(只针对某个网站内部进行搜索)。 实现一个 Java API 文档的简单的搜

    2023年04月08日
    浏览(42)
  • 超强文档搜索引擎AnyTXT Searcher本地搭建

    你是否遇到过这种情况,异地办公或者不在公司,想找到一篇课件或者想找到某个文件,你只记得资料文件或者课件里的某一句话,却不记得它的名字,你花了大量的时间,大量的精力,却怎么也找不到这个文件在哪个地方,看完本篇文章,希望能解决你的这个问题!Any TX

    2024年01月17日
    浏览(34)
  • 微服务分布式搜索引擎 ElasticSearch 查询文档

    本文参考黑马 分布式Elastic search Elasticsearch是一款非常强大的开源搜索引擎,具备非常多强大功能,可以帮助我们从海量数据中快速找到需要的内容 ElasticSearch 的查询依然是基于 JSON风格的DSL 来实现的。 Elasticsearch提供了基于JSON的DSL(Domain Specific Language)来定义查询。常见的

    2023年04月15日
    浏览(47)
  • 分布式搜索引擎ElasticSearch的RestClient查询文档

         

    2024年02月12日
    浏览(62)
  • 【搜索引擎】Document indexing and retrieval: 文档索引与检索

    作者:禅与计算机程序设计艺术 搜索引擎作为互联网信息获取的一种重要手段之一,无论是在PC、移动端还是电脑上使用,都可以快速找到想要的信息。而对于文档信息的搜索引擎索引构建,则是一个更加复杂的问题。 文档索引与检索(Document Indexing and Retrieval, DIR)的目标是建

    2024年02月08日
    浏览(39)
  • 微服务分布式搜索引擎 Elastic Search RestClient 操作文档

    本文参考黑马 分布式Elastic search Elasticsearch是一款非常强大的开源搜索引擎,具备非常多强大功能,可以帮助我们从海量数据中快速找到需要的内容 初始化RestHighLevelClient 为了与索引库操作分离,我们再次参加一个测试类,做两件事情: 初始化RestHighLevelClient 我们的酒店数据

    2024年01月24日
    浏览(38)
  • 搜索引擎onesearch 2.0分布式文档索引设计+tika原理源码分析

    《搜索引擎onesearch1.0-设计与实现.docx》介绍了1.0特性,表达式搜索,搜索schema,agg,映射等,同时附录介绍未来规划,其主要特性是文档索引,随着分布式dataX完成,技术基础已完备。 本文介绍分布式文档索引,包括tika的原理源码分析 Tika原理源码分析 , 内容类型识别,内

    2024年02月06日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包