You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

search.go 16 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  1. package routers
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "strconv"
  6. "strings"
  7. "code.gitea.io/gitea/models"
  8. "code.gitea.io/gitea/modules/context"
  9. "code.gitea.io/gitea/modules/log"
  10. "code.gitea.io/gitea/modules/setting"
  11. "github.com/olivere/elastic/v7"
  12. )
  13. type SearchRes struct {
  14. Total int64
  15. Result []map[string]interface{}
  16. }
  17. var client *elastic.Client
  18. func InitESClient() {
  19. ESSearchUrl := setting.ESSearchURL
  20. var err error
  21. client, err = elastic.NewClient(elastic.SetSniff(false), elastic.SetURL(ESSearchUrl))
  22. if err != nil {
  23. panic(err)
  24. }
  25. }
  26. func Search(ctx *context.Context) {
  27. TableName := ctx.Query("TableName")
  28. Key := ctx.Query("Key")
  29. Page := ctx.QueryInt("Page")
  30. PageSize := ctx.QueryInt("PageSize")
  31. if Page <= 0 {
  32. Page = 1
  33. }
  34. if PageSize <= 0 || PageSize > 200 {
  35. PageSize = setting.UI.IssuePagingNum
  36. }
  37. if TableName == "repository" {
  38. searchRepo(ctx, "repository-es-index", Key, Page, PageSize)
  39. return
  40. } else if TableName == "issue" {
  41. searchIssue(ctx, "issue-es-index", Key, Page, PageSize)
  42. return
  43. } else if TableName == "user" {
  44. searchUserOrOrg(ctx, "user-es-index", Key, Page, PageSize, true)
  45. return
  46. } else if TableName == "org" {
  47. searchUserOrOrg(ctx, "user-es-index", Key, Page, PageSize, false)
  48. return
  49. } else if TableName == "dataset" {
  50. searchDataSet(ctx, "dataset-es-index", Key, Page, PageSize)
  51. return
  52. } else if TableName == "pr" {
  53. searchPR(ctx, "issue-es-index", Key, Page, PageSize)
  54. return
  55. }
  56. }
  57. func searchRepoByLabel(ctx *context.Context, TableName string, Key string, Page int, PageSize int) {
  58. /*
  59. 项目, ES名称: repository-es-index
  60. 搜索:
  61. name character varying(255) , 项目名称
  62. description text, 项目描述
  63. topics json, 标签
  64. 排序:
  65. updated_unix
  66. num_watches,
  67. num_stars,
  68. num_forks,
  69. */
  70. SortBy := ctx.Query("SortBy")
  71. if SortBy == "" {
  72. SortBy = "updated_unix.keyword"
  73. }
  74. ascending := ctx.QueryBool("Ascending")
  75. log.Info("query searchRepoByLabel start")
  76. if Key != "" {
  77. boolQ := elastic.NewBoolQuery()
  78. topicsQuery := elastic.NewMatchQuery("topics", Key)
  79. boolQ.Should(topicsQuery)
  80. res, err := client.Search(TableName).Query(boolQ).Sort(SortBy, ascending).From((Page - 1) * PageSize).Size(PageSize).Do(ctx.Req.Context())
  81. if err == nil {
  82. result := makeRepoResult(res, "")
  83. ctx.JSON(200, result)
  84. return
  85. } else {
  86. log.Info("query es error," + err.Error())
  87. }
  88. }
  89. ctx.JSON(200, "")
  90. }
  91. func searchRepo(ctx *context.Context, TableName string, Key string, Page int, PageSize int) {
  92. /*
  93. 项目, ES名称: repository-es-index
  94. 搜索:
  95. name character varying(255) , 项目名称
  96. description text, 项目描述
  97. topics json, 标签
  98. 排序:
  99. updated_unix
  100. num_watches,
  101. num_stars,
  102. num_forks,
  103. */
  104. SortBy := ctx.Query("SortBy")
  105. if SortBy == "" {
  106. SortBy = "updated_unix.keyword"
  107. }
  108. ascending := ctx.QueryBool("Ascending")
  109. log.Info("query searchRepo start")
  110. if Key != "" {
  111. boolQ := elastic.NewBoolQuery()
  112. nameQuery := elastic.NewMatchQuery("name", Key).Boost(1024).QueryName("f_first")
  113. descriptionQuery := elastic.NewMatchQuery("description", Key).Boost(1.5).QueryName("f_second")
  114. topicsQuery := elastic.NewMatchQuery("topics", Key).Boost(1).QueryName("f_third")
  115. boolQ.Should(nameQuery, descriptionQuery, topicsQuery)
  116. res, err := client.Search(TableName).Query(boolQ).SortBy(elastic.NewScoreSort(), elastic.NewFieldSort(SortBy).Order(ascending)).From((Page - 1) * PageSize).Size(PageSize).Do(ctx.Req.Context())
  117. if err == nil {
  118. result := makeRepoResult(res, Key)
  119. ctx.JSON(200, result)
  120. } else {
  121. log.Info("query es error," + err.Error())
  122. ctx.JSON(200, "")
  123. }
  124. } else {
  125. log.Info("query all content.")
  126. //搜索的属性要指定{"timestamp":{"unmapped_type":"date"}}
  127. res, err := client.Search(TableName).SortBy(elastic.NewFieldSort(SortBy).Order(ascending)).From((Page - 1) * PageSize).Size(PageSize).Do(ctx.Req.Context())
  128. if err == nil {
  129. result := makeRepoResult(res, "")
  130. ctx.JSON(200, result)
  131. } else {
  132. log.Info("query es error," + err.Error())
  133. ctx.JSON(200, "")
  134. }
  135. }
  136. }
  137. func makeRepoResult(sRes *elastic.SearchResult, Key string) *SearchRes {
  138. total := sRes.Hits.TotalHits.Value
  139. result := make([]map[string]interface{}, 0)
  140. for i, hit := range sRes.Hits.Hits {
  141. log.Info("this is repo query " + fmt.Sprint(i) + " result.")
  142. recordSource := make(map[string]interface{})
  143. source, err := hit.Source.MarshalJSON()
  144. if err == nil {
  145. err = json.Unmarshal(source, &recordSource)
  146. if err == nil {
  147. record := make(map[string]interface{})
  148. record["name"] = recordSource["name"]
  149. record["owner_name"] = recordSource["owner_name"]
  150. desc := recordSource["description"].(string)
  151. record["description"] = dealLongText(desc, Key, hit.MatchedQueries)
  152. record["num_watches"] = recordSource["num_watches"]
  153. record["num_stars"] = recordSource["num_stars"]
  154. record["num_forks"] = recordSource["num_forks"]
  155. record["topics"] = recordSource["topics"]
  156. if recordSource["avatar"] != nil {
  157. record["avatar"] = setting.AppSubURL + "/repo-avatars/" + recordSource["avatar"].(string)
  158. }
  159. record["updated_unix"] = recordSource["updated_unix"]
  160. record["lang"] = recordSource["lang"]
  161. result = append(result, record)
  162. } else {
  163. log.Info("deal repo source error," + err.Error())
  164. }
  165. } else {
  166. log.Info("deal repo source error," + err.Error())
  167. }
  168. }
  169. returnObj := &SearchRes{
  170. Total: total,
  171. Result: result,
  172. }
  173. return returnObj
  174. }
  175. func dealLongText(text string, Key string, MatchedQueries []string) string {
  176. var isNeedToDealText bool
  177. isNeedToDealText = false
  178. if len(MatchedQueries) > 0 && Key != "" {
  179. if MatchedQueries[0] == "f_second" || MatchedQueries[0] == "f_third" {
  180. isNeedToDealText = true
  181. }
  182. }
  183. stringlen := len(text)
  184. if isNeedToDealText && stringlen > 200 {
  185. index := strings.Index(text, Key)
  186. if index > 0 {
  187. start := index - 50
  188. if start < 0 {
  189. start = 0
  190. }
  191. end := index + 150
  192. if end >= stringlen {
  193. end = stringlen
  194. }
  195. return text[start:end]
  196. } else {
  197. return text[0:200]
  198. }
  199. } else {
  200. if stringlen > 200 {
  201. return text[0:200]
  202. } else {
  203. return text
  204. }
  205. }
  206. }
  207. func searchUserOrOrg(ctx *context.Context, TableName string, Key string, Page int, PageSize int, IsQueryUser bool) {
  208. /*
  209. 用户或者组织 ES名称: user-es-index
  210. 搜索:
  211. name , 名称
  212. full_name 全名
  213. description 描述或者简介
  214. 排序:
  215. created_unix
  216. 名称字母序
  217. */
  218. SortBy := ctx.Query("SortBy")
  219. if SortBy == "" {
  220. SortBy = "updated_unix.keyword"
  221. }
  222. ascending := ctx.QueryBool("Ascending")
  223. boolQ := elastic.NewBoolQuery()
  224. if Key != "" {
  225. nameQuery := elastic.NewMatchQuery("name", Key).Boost(2).QueryName("f_first")
  226. full_nameQuery := elastic.NewMatchQuery("full_name", Key).Boost(1.5).QueryName("f_second")
  227. descriptionQuery := elastic.NewMatchQuery("description", Key).Boost(1).QueryName("f_third")
  228. boolQ.Should(nameQuery, full_nameQuery, descriptionQuery)
  229. }
  230. typeValue := 1
  231. if IsQueryUser {
  232. typeValue = 0
  233. }
  234. UserOrOrgQuery := elastic.NewTermQuery("type", typeValue)
  235. boolQ.Must(UserOrOrgQuery)
  236. res, err := client.Search(TableName).Query(boolQ).Sort(SortBy, ascending).From((Page - 1) * PageSize).Size(PageSize).Do(ctx.Req.Context())
  237. if err == nil {
  238. result := makeUserOrOrgResult(res, Key, ctx)
  239. ctx.JSON(200, result)
  240. } else {
  241. log.Info("query es error," + err.Error())
  242. ctx.JSON(200, "")
  243. }
  244. }
  245. func makeUserOrOrgResult(sRes *elastic.SearchResult, Key string, ctx *context.Context) *SearchRes {
  246. total := sRes.Hits.TotalHits.Value
  247. result := make([]map[string]interface{}, 0)
  248. for i, hit := range sRes.Hits.Hits {
  249. log.Info("this is user query " + fmt.Sprint(i) + " result.")
  250. recordSource := make(map[string]interface{})
  251. source, err := hit.Source.MarshalJSON()
  252. if err == nil {
  253. err = json.Unmarshal(source, &recordSource)
  254. if err == nil {
  255. record := make(map[string]interface{})
  256. record["name"] = recordSource["name"]
  257. record["full_name"] = recordSource["full_name"]
  258. desc := recordSource["description"].(string)
  259. record["description"] = dealLongText(desc, Key, hit.MatchedQueries)
  260. if ctx.User != nil {
  261. record["email"] = recordSource["email"]
  262. } else {
  263. record["email"] = ""
  264. }
  265. record["location"] = recordSource["location"]
  266. record["website"] = recordSource["website"]
  267. record["num_repos"] = recordSource["num_repos"]
  268. record["num_teams"] = recordSource["num_teams"]
  269. record["num_members"] = recordSource["num_members"]
  270. record["avatar"] = strings.TrimRight(setting.AppSubURL, "/") + "/user/avatar/" + recordSource["name"].(string) + "/" + strconv.Itoa(-1)
  271. record["updated_unix"] = recordSource["updated_unix"]
  272. record["created_unix"] = recordSource["created_unix"]
  273. result = append(result, record)
  274. } else {
  275. log.Info("deal user source error," + err.Error())
  276. }
  277. } else {
  278. log.Info("deal user source error," + err.Error())
  279. }
  280. }
  281. returnObj := &SearchRes{
  282. Total: total,
  283. Result: result,
  284. }
  285. return returnObj
  286. }
  287. func searchDataSet(ctx *context.Context, TableName string, Key string, Page int, PageSize int) {
  288. /*
  289. 数据集,ES名称:dataset-es-index
  290. 搜索:
  291. title , 名称
  292. description 描述
  293. category 标签
  294. file_name 数据集文件名称
  295. 排序:
  296. download_times
  297. */
  298. SortBy := ctx.Query("SortBy")
  299. if SortBy == "" {
  300. SortBy = "download_times.keyword"
  301. }
  302. ascending := ctx.QueryBool("Ascending")
  303. log.Info("query searchRepo start")
  304. boolQ := elastic.NewBoolQuery()
  305. if Key != "" {
  306. nameQuery := elastic.NewMatchQuery("title", Key).Boost(2).QueryName("f_first")
  307. descQuery := elastic.NewMatchQuery("description", Key).Boost(1.5).QueryName("f_second")
  308. fileNameQuery := elastic.NewMatchQuery("file_name", Key).Boost(1).QueryName("f_third")
  309. categoryQuery := elastic.NewMatchQuery("category", Key).Boost(1).QueryName("f_fourth")
  310. boolQ.Should(nameQuery, descQuery, categoryQuery, fileNameQuery)
  311. res, err := client.Search(TableName).Query(boolQ).Sort(SortBy, ascending).From((Page - 1) * PageSize).Size(PageSize).Do(ctx.Req.Context())
  312. if err == nil {
  313. result := makeDatasetResult(res, Key)
  314. ctx.JSON(200, result)
  315. } else {
  316. log.Info("query es error," + err.Error())
  317. }
  318. } else {
  319. log.Info("query all content.")
  320. //搜索的属性要指定{"timestamp":{"unmapped_type":"date"}}
  321. res, err := client.Search(TableName).Sort(SortBy, ascending).From((Page - 1) * PageSize).Size(PageSize).Do(ctx.Req.Context())
  322. if err == nil {
  323. result := makeRepoResult(res, "")
  324. ctx.JSON(200, result)
  325. } else {
  326. log.Info("query es error," + err.Error())
  327. ctx.JSON(200, "")
  328. }
  329. }
  330. }
  331. func makeDatasetResult(sRes *elastic.SearchResult, Key string) *SearchRes {
  332. total := sRes.Hits.TotalHits.Value
  333. result := make([]map[string]interface{}, 0)
  334. for i, hit := range sRes.Hits.Hits {
  335. log.Info("this is dataset query " + fmt.Sprint(i) + " result.")
  336. recordSource := make(map[string]interface{})
  337. source, err := hit.Source.MarshalJSON()
  338. if err == nil {
  339. err = json.Unmarshal(source, &recordSource)
  340. if err == nil {
  341. record := make(map[string]interface{})
  342. record["id"] = recordSource["id"]
  343. userId := recordSource["user_id"].(int64)
  344. user, errUser := models.GetUserByID(userId)
  345. if errUser == nil {
  346. record["owerName"] = user.GetDisplayName()
  347. record["avatar"] = user.RelAvatarLink()
  348. }
  349. record["title"] = recordSource["title"]
  350. record["category"] = recordSource["category"]
  351. desc := recordSource["description"].(string)
  352. record["description"] = dealLongText(desc, Key, hit.MatchedQueries)
  353. record["download_times"] = recordSource["download_times"]
  354. record["created_unix"] = recordSource["created_unix"]
  355. result = append(result, record)
  356. } else {
  357. log.Info("deal dataset source error," + err.Error())
  358. }
  359. } else {
  360. log.Info("deal dataset source error," + err.Error())
  361. }
  362. }
  363. returnObj := &SearchRes{
  364. Total: total,
  365. Result: result,
  366. }
  367. return returnObj
  368. }
  369. func searchIssue(ctx *context.Context, TableName string, Key string, Page int, PageSize int) {
  370. /*
  371. 任务,合并请求 ES名称:issue-es-index
  372. 搜索:
  373. name character varying(255) , 标题
  374. content text, 内容
  375. comment text, 评论
  376. 排序:
  377. updated_unix
  378. */
  379. SortBy := ctx.Query("SortBy")
  380. if SortBy == "" {
  381. SortBy = "updated_unix.keyword"
  382. }
  383. ascending := ctx.QueryBool("Ascending")
  384. boolQ := elastic.NewBoolQuery()
  385. if Key != "" {
  386. nameQuery := elastic.NewMatchQuery("name", Key).Boost(2).QueryName("f_first")
  387. contentQuery := elastic.NewMatchQuery("content", Key).Boost(1.5).QueryName("f_second")
  388. commentQuery := elastic.NewMatchQuery("comment", Key).Boost(1).QueryName("f_third")
  389. boolQ.Should(nameQuery, contentQuery, commentQuery)
  390. }
  391. isIssueQuery := elastic.NewTermQuery("is_pull", "f")
  392. boolQ.Must(isIssueQuery)
  393. res, err := client.Search(TableName).Query(boolQ).Sort(SortBy, ascending).From((Page - 1) * PageSize).Size(PageSize).Do(ctx.Req.Context())
  394. if err == nil {
  395. result := makeIssueResult(res, Key)
  396. ctx.JSON(200, result)
  397. } else {
  398. log.Info("query es error," + err.Error())
  399. }
  400. }
  401. func makeIssueResult(sRes *elastic.SearchResult, Key string) *SearchRes {
  402. total := sRes.Hits.TotalHits.Value
  403. result := make([]map[string]interface{}, 0)
  404. for i, hit := range sRes.Hits.Hits {
  405. log.Info("this is issue query " + fmt.Sprint(i) + " result.")
  406. recordSource := make(map[string]interface{})
  407. source, err := hit.Source.MarshalJSON()
  408. if err == nil {
  409. err = json.Unmarshal(source, &recordSource)
  410. if err == nil {
  411. record := make(map[string]interface{})
  412. record["id"] = recordSource["id"]
  413. record["repo_id"] = recordSource["repo_id"]
  414. log.Info("recordSource[\"repo_id\"]=" + fmt.Sprint(recordSource["repo_id"]))
  415. repo, errRepo := models.GetRepositoryByID(recordSource["repo_id"].(int64))
  416. if errRepo == nil {
  417. record["repoUrl"] = repo.FullName()
  418. record["avatar"] = repo.RelAvatarLink()
  419. }
  420. record["name"] = recordSource["name"]
  421. desc := recordSource["content"].(string)
  422. record["content"] = dealLongText(desc, Key, hit.MatchedQueries)
  423. if recordSource["pr_id"] != nil {
  424. record["pr_id"] = recordSource["pr_id"]
  425. }
  426. desc = recordSource["comment"].(string)
  427. record["comment"] = dealLongText(desc, Key, hit.MatchedQueries)
  428. record["num_comments"] = recordSource["num_comments"]
  429. record["updated_unix"] = recordSource["updated_unix"]
  430. result = append(result, record)
  431. } else {
  432. log.Info("deal issue source error," + err.Error())
  433. }
  434. } else {
  435. log.Info("deal issue source error," + err.Error())
  436. }
  437. }
  438. returnObj := &SearchRes{
  439. Total: total,
  440. Result: result,
  441. }
  442. return returnObj
  443. }
  444. func searchPR(ctx *context.Context, TableName string, Key string, Page int, PageSize int) {
  445. /*
  446. 任务,合并请求 ES名称:issue-es-index
  447. 搜索:
  448. name character varying(255) , 标题
  449. content text, 内容
  450. comment text, 评论
  451. 排序:
  452. updated_unix
  453. */
  454. SortBy := ctx.Query("SortBy")
  455. if SortBy == "" {
  456. SortBy = "updated_unix.keyword"
  457. }
  458. ascending := ctx.QueryBool("Ascending")
  459. boolQ := elastic.NewBoolQuery()
  460. if Key != "" {
  461. nameQuery := elastic.NewMatchQuery("name", Key).Boost(1024).QueryName("f_first")
  462. contentQuery := elastic.NewMatchQuery("content", Key).Boost(1.5).QueryName("f_second")
  463. commentQuery := elastic.NewMatchQuery("comment", Key).Boost(1).QueryName("f_third")
  464. boolQ.Should(nameQuery, contentQuery, commentQuery)
  465. }
  466. isIssueQuery := elastic.NewTermQuery("is_pull", "t")
  467. boolQ.Must(isIssueQuery)
  468. res, err := client.Search(TableName).Query(boolQ).Sort(SortBy, ascending).From((Page - 1) * PageSize).Size(PageSize).Do(ctx.Req.Context())
  469. if err == nil {
  470. result := makeIssueResult(res, Key)
  471. ctx.JSON(200, result)
  472. } else {
  473. log.Info("query es error," + err.Error())
  474. }
  475. }