[笔记] Google Zoekt 代码搜索引擎代码手册

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章