当前位置: 代码网 > it编程>软件设计>数据结构 > 【项目日记(三)】搜索引擎-搜索模块

【项目日记(三)】搜索引擎-搜索模块

2024年08月03日 数据结构 我要评论
这篇文章主要介绍了,搜索引擎的搜锁模块,这部分的难点主要是去重操作,去重的时候需要用到我们之前学过的数据结构,小根堆结合多个有序数组完成去重操作!

在这里插入图片描述

1.前言

2.项目回顾

到目前为止,我们已经实现了2个类,parser和index。
实现parser类:
1.通过递归枚举出所有的html文件。
2.针对每一个html进行解析操作。
a)标题:直接使用文件名称
b)url:基于文件路径进行简单的字符串拼接
c)正文:去掉script和html标签
3.把解析内容通过adddoc放入index类中

实现index类:
正排索引:arraylist
倒排索引:hashmap<string,arraylist>
1.查正排:直接按照下标来取arraylist中的元素
2.查倒排:直接按照key,来区hashmap中的元素
3.添加文档,供parser类调用
a)构建正排索引,构造docinfo对象,添加到索引末尾
b)构建倒排索引,先对标题,正文进行分词操作,统计词频,添加到map中去
4.保存索引:基于json格式把索引数据保存到指定文件中。
5.加载索引:基于json格式对数据进行解析,存入内存。

3.搜索流程

  • 1.【分词】根据输入内容进行分词操作
  • 2.【触发】针对分词结果来查倒排
  • 3.【去重】针对相同的文档进行去重
  • 4.【排序】针对去重结果按照权重排序
  • 5.【包装】针对排序结果查正牌,包装为result进行返回数据

3.1分词

在使用ansj技术进行分词操作的时候,会把空格,以及一些高频词例如a,an,is 等词语都分出来,但这些词语和我们的查询内容关联性并不大,我们就单独罗列出来,进行排除。网上有许多暂停词表可以自行下载,例如:
在这里插入图片描述

private static  string stop_word_path="d:/doc_searcher_index/stop_word.txt";
private hashset<string> stopwords=new hashset<>();
public docsearcher(){
        index.load();
        loadstopwords();
    }
public void loadstopwords(){
        try (bufferedreader bufferedreader=new bufferedreader(new filereader(stop_word_path))){
           while (true){
               string line=bufferedreader.readline();
               if (line==null){
                   break;
               }
               stopwords.add(line);
           }
        } catch (ioexception e) {
            throw new runtimeexception(e);
        }
    }    
list<term> oldterms=toanalysis.parse(query).getterms();
        list<term> terms=new arraylist<>();
        for (term term:oldterms){
            if (stopwords.contains(term.getname())){
                continue;
            }
            terms.add(term);
        }          

3.2触发

 list<list<weight>> termresult=new arraylist<>();
        for (term term:terms){
            string word=term.getname();
            list<weight> invertedlist=index.getinverted(word);
            if (invertedlist==null){
                continue;
            }
            termresult.add(invertedlist);
        }

3.3去重

前面,我们对用户输入的结果进行触发操作的时候,一个词可能出现在多个文档中,同理,一个文档也可能存在多个分词结果,如果我们不对相同的文档进行去重,那么一个文档针对不同的分词结果就会出现多次,这样显然不合理的。索引我们需要对相同的文档进行去重。那么具体该如何操作呢?触发的结果是一个二维数组,可以利用两个有序数组排序的思想进行去重,只不过这里运用的是多个有序数组排序。

  • 1.针对每一行按照升序排序
  • 2.借助优先级队列,争对多个有序数组进行合并
  • 3.初始化队列,把每一行第一个元素放入队列
  • 4.循环的取每行首个元素,遇到相同的docid,权重相加
 list<weight> alltermresult=mergeresult(termresult);
  static class pos{
        public int row;
        public int col;
        public pos(int row, int col) {
            this.row = row;
            this.col = col;
        }
    }
    private list<weight> mergeresult(list<list<weight>> source) {
    //1.针对每一行按照升序排序
        for (list<weight> currow:source){
            currow.sort(new comparator<weight>() {
                @override
                public int compare(weight o1, weight o2) {
                    return o1.getdocid()-o2.getdocid();
                }
            });
        }
    //2.借助优先级队列,争对多个有序数组进行合并
        list<weight> target=new arraylist<>();
      priorityqueue<pos> queue=new priorityqueue<>(new comparator<pos>() {
          @override
          public int compare(pos o1, pos o2) {
              weight w1=source.get(o1.row).get(o1.col);
              weight w2=source.get(o2.row).get(o2.col);
              return w1.getdocid()-w2.getdocid();
          }
      });
      //3.初始化队列,把每一行第一个元素放入队列
        for (int row=0;row<source.size();row++){
            queue.offer(new pos(row,0));
        }
      //循环的取每行首个元素
      while (!queue.isempty()){
          pos minpos=queue.poll();
          weight curweight=source.get(minpos.row).get(minpos.col);
          if (target.size()>0){
              weight lastweight=target.get(target.size()-1);
              //遇到相同的文章,权重相加
              if (lastweight.getdocid()==curweight.getdocid()){
                  lastweight.setweight(lastweight.getweight()+curweight.getweight());
              }else {
                  target.add(curweight);
              }
          }else {
              target.add(curweight);
          }
          pos newpos=new pos(minpos.row,minpos.col+1);
          if (newpos.col>=source.get(newpos.row).size()){
              continue;
          }
          queue.offer(newpos);
      }
      return target;
    }

3.4排序

  alltermresult.sort(new comparator<weight>() {
            @override
            public int compare(weight o1, weight o2) {
                //按照降序排序
                return o2.getweight()-o1.getweight();
            }
        });

3.5包装

需要注意的是返回的结果为标题,url,描述,而描述不能直接把正文返回,而是返回一段包含用户分词结果的一小段描述。生成描述的思路:可以回去到所有分词结果,遍历分词结果,看哪个结果在正文中出现,那么直接截取分词的前10个字符和后160个字符来进行描述。

public class result {
    private string title;
    private string url;
    private string desc;
    @override
    public string tostring() {
        return "result{" +
                "title='" + title + '\'' +
                ", url='" + url + '\'' +
                ", desc='" + desc + '\'' +
                '}';
    }
    public string gettitle() {
        return title;
    }
    public void settitle(string title) {
        this.title = title;
    }
    public string geturl() {
        return url;
    }
    public void seturl(string url) {
        this.url = url;
    }
    public string getdesc() {
        return desc;
    }
    public void setdesc(string desc) {
        this.desc = desc;
    }
}

list<result> results=new arraylist<>();
        for (weight weight:alltermresult){
            docinfo docinfo=index.getdocinfo(weight.getdocid());
            result result=new result();
            result.settitle(docinfo.gettitle());
            result.seturl(docinfo.geturl());
            result.setdesc(gendesc(docinfo.getcontent(),terms));
            results.add(result);
        }
private string gendesc(string content, list<term> terms) {
        int firstpos=-1;
        for (term term:terms){
            string word=term.getname();
            //避免出现word前后带有标点符号
            content=content.tolowercase().replaceall("\\b"+word+"\\b"," "+word+" ");
            firstpos=content.indexof(" "+word+" ");
            if (firstpos>=0){
                break;
            }
        }
        if (firstpos==-1){
            if (content.length()>160){
                return content.substring(0,160)+"...";
            }
            return content;
        }
        string desc="";
        int descbeg=firstpos<60?0:firstpos-60;
        if (descbeg+160>content.length()){
            desc=content.substring(descbeg);
        }else {
            desc=content.substring(descbeg,descbeg+160)+"...";
        }
        //把描述中和分词结果相同的部分设置为斜体加上<i>标签,方便前端标红
        for (term term:terms){
            string word=term.getname();
            //进行忽略大小写的全词匹配
            desc=desc.replaceall("(?i) "+word+" ","<i> "+word+" </i>");
        }
        return desc;
    }

4.总结

下期预告:搜索引擎(四)

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com