OpenGrok的索引实在是难用,使用了Lucene,但是还停留在TD.IDF上,搜索词语的一部分是没有结果的…所以转到SourceGraph。SourceGraph不上language server跳转准确度惊人的低。那么我只想要它的搜索功能怎么办?进SourceGraph的一个container,ps
一下,发现它使用了Google的一个业余小项目zoekt
。代码也不多,而且是tri-gram
能准确在代码中搜索一些诸如 ' + 'test'
这样的字符串了。果断fork了。
(文章会不定期更新)
我的fork: https://github.com/dna2fork/zoekt
Google:https://github.com/google/zoekt
支持Json输出
首先第一个要解决的是输出Json的问题,如何让zoekt把结果按照json的格式输出呢?默认它是html。我们找到了源代码下 ./web/templates.go
下面有不少html模版。寻着代码读过去,它可以在zoekt-webserver
启动的时候指定一个文件夹放模版文件。兴冲冲就去写了一个json的模版,放上去不错,直接work了。但是有些时候发现一直出错…Json格式不对?原来是有特殊json的控制字符的时候导致输出混乱了,所以得想办法预先将输出的字符串格式化一下。
./web/server.go
里加一个专门处理Json的函数,然后在template里就可以用这个函数format数据啦~
+ "JsonText": func(json string) string {
+ json = strings.Replace(json, "\\", "\", -1)
+ json = strings.Replace(json, "\n", " ", -1)
+ json = strings.Replace(json, "\r", " ", -1)
+ json = strings.Replace(json, "\t", "", -1)
+ return json
+ },
支持搜索多个repo
第二个解决的是,用zoekt的时候我不能用r:...
去同时搜索多个repo,只能一个一个搜索,这很恼人。看看它是如何工作的吧。首先搜索一定是parser然后matcher再sorter。于是看./query/parse.go
case tokRepo:
expr = &Repo{Pattern: text}
它是把r:...
转成一个表达式的node,这个Repo
是定义在./query/query.go
里的,再查它用在哪里——./eval.go
if r, ok := q.(*query.Repo); ok {
return &query.Const{Value: strings.Contains(d.repoMetaData.Name, r.Pattern)}
}
然后就是直接判断r:
里的字符串是不是被包含在repo名字里。这简单了,把这个Pattern当个string list就可以了呀,就是先split,然后判断每个split后的片段是不是被包含,只要有一个被包含就去true。直接改一波。
func buildRepoListPattern(qStr string) ([]string, error) {
// TODO: validate qStr (length, split(',') length) if too long return error
if len(qStr) == 0 {
return nil, nil
}
patterns := strings.Split(qStr, ",")
return patterns, nil
}
func matchRepoListPattern(name string, patternList []string) bool {
if (patternList == nil) {
return true
}
if (len(patternList) == 0) {
return true
}
for _, a := range patternList {
if strings.Contains(name, a) {
return true
}
}
return false
}
func (d *indexData) simplify(in query.Q) query.Q {
eval := query.Map(in, func(q query.Q) query.Q {
if r, ok := q.(*query.Repo); ok {
// original: return &query.Const{Value: strings.Contains(d.repoMetaData.Name, r.Pattern)}
if r.RepoNamesFromPattern == nil {
r.RepoNamesFromPattern, _ = buildRepoListPattern(r.Pattern)
}
return &query.Const{Value: matchRepoListPattern(d.repoMetaData.Name, r.RepoNamesFromPattern)}
}
if l, ok := q.(*query.Language); ok {
_, has := d.metaData.LanguageMap[l.Language]
if !has {
return &query.Const{Value: false}
}
}
return q
})
return query.Simplify(eval)
}
编译,重启,好了可以搜索多repo了。
J.Y.Liu
2020.01.06