opengrok源码浅析

最近公司想搭一套代码搜索系统,所以调研了一下Oracle的开源代码搜索系统opengrok的索引建立机制。

opengrok建立索引主要架构图如下所示:


入口在Indexer类中,其中Indexer会对每个project建立一个索引类IndexDatabase,IndexDatabase通过遍历代码目录下的所有文件对每个文件生成索引。IndexDatabase中的indexDown是建立索引的核心方法,对每个文件进行三层解析,第一层解析出文件本身的一些属性(文件名,目录等),第二层用ctags解析出代码中的所有变量定义,第三层用源文件对应对解析器解析出源文件中的所有变量引用,最终把所有解析出来的属性存在一个lucene的Document对象中。

第一层解析关键代码

doc.add(new Field(QueryBuilder.U, Util.path2uid(path, date),
                string_ft_stored_nanalyzed_norms));
        doc.add(new Field(QueryBuilder.FULLPATH, file.getAbsolutePath(),
                string_ft_nstored_nanalyzed_norms));
        doc.add(new SortedDocValuesField(QueryBuilder.FULLPATH, new BytesRef(file.getAbsolutePath())));

        try {
            HistoryReader hr = HistoryGuru.getInstance().getHistoryReader(file);
            if (hr != null) {
                doc.add(new TextField(QueryBuilder.HIST, hr));
                // date = hr.getLastCommentDate() //RFE
            }
        } catch (HistoryException e) {
            LOGGER.log(Level.WARNING, "An error occurred while reading history: ", e);
        }
        doc.add(new Field(QueryBuilder.DATE, date, string_ft_stored_nanalyzed_norms));
        doc.add(new SortedDocValuesField(QueryBuilder.DATE, new BytesRef(date)));
        if (path != null) {
            doc.add(new TextField(QueryBuilder.PATH, path, Store.YES));
            Project project = Project.getProject(path);
            if (project != null) {
                doc.add(new TextField(QueryBuilder.PROJECT, project.getPath(), Store.YES));
            }
        }

其中doc就是lucene的Document,可以看到第一层是解析出了文件的通用的一些信息。


第二和第三层的解析代码:

doc.add(new TextField(QueryBuilder.FULL, getReader(src.getStream())));
        String fullpath = doc.get(QueryBuilder.FULLPATH);
        if (fullpath != null && ctags != null) {
            defs = ctags.doCtags(fullpath + "\n");
            if (defs != null && defs.numberOfSymbols() > 0) {
                doc.add(new TextField(QueryBuilder.DEFS, new IteratorReader(defs.getSymbols())));
                //this is to explicitly use appropriate analyzers tokenstream to workaround #1376 symbols search works like full text search 
                TextField ref=new TextField(QueryBuilder.REFS,this.SymbolTokenizer);
                this.SymbolTokenizer.setReader(getReader(src.getStream()));
                doc.add(ref);
                byte[] tags = defs.serialize();
                doc.add(new StoredField(QueryBuilder.TAGS, tags));                
            }
        }
        
        if (scopesEnabled && xrefOut == null) {
            /*
             * Scopes are generated during xref generation. If xrefs are
             * turned off we still need to run writeXref to produce scopes,
             * we use a dummy writer that will throw away any xref output.
             */
            xrefOut = new NullWriter();
        }

        if (xrefOut != null) {
            try (Reader in = getReader(src.getStream())) {
                writeXref(in, xrefOut);
            }
            
            Scopes scopes = xref.getScopes();
            if (scopes.size() > 0) {
                byte[] scopesSerialized = scopes.serialize();
                doc.add(new StoredField(QueryBuilder.SCOPES, scopesSerialized));
            }
        }

可以看到其中区分了代码中的引用(ref)和定义(def)。

最终生成的doc中其实就包含了一个源文件中的若干属性字段,每个字段都可以暴露给客户端进行代码搜索的条件。


结语:代码搜索系统的核心就是根据代码文件的各种属性建立索引,提供给客户端查询,如果做深入的话需要结合语法进行索引,比如有哪些类定义哪些枚举定义有哪些变量定义都可以单独建立索引,opengrok中还有一个很重要的部分是处理文件历史版本以及变化更新索引的机制,这部分暂时还看的不太懂,有时间再继续研究。


  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值