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 17 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  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["id"] = hit.Id
  149. record["name"] = recordSource["name"]
  150. record["owner_name"] = recordSource["owner_name"]
  151. if recordSource["description"] != nil {
  152. desc := recordSource["description"].(string)
  153. record["description"] = dealLongText(desc, Key, hit.MatchedQueries)
  154. } else {
  155. record["description"] = ""
  156. }
  157. record["num_watches"] = recordSource["num_watches"]
  158. record["num_stars"] = recordSource["num_stars"]
  159. record["num_forks"] = recordSource["num_forks"]
  160. record["topics"] = recordSource["topics"]
  161. if recordSource["avatar"] != nil {
  162. record["avatar"] = setting.AppSubURL + "/repo-avatars/" + recordSource["avatar"].(string)
  163. }
  164. record["updated_unix"] = recordSource["updated_unix"]
  165. record["lang"] = recordSource["lang"]
  166. result = append(result, record)
  167. } else {
  168. log.Info("deal repo source error," + err.Error())
  169. }
  170. } else {
  171. log.Info("deal repo source error," + err.Error())
  172. }
  173. }
  174. returnObj := &SearchRes{
  175. Total: total,
  176. Result: result,
  177. }
  178. return returnObj
  179. }
  180. func dealLongText(text string, Key string, MatchedQueries []string) string {
  181. var isNeedToDealText bool
  182. isNeedToDealText = false
  183. if len(MatchedQueries) > 0 && Key != "" {
  184. if MatchedQueries[0] == "f_second" || MatchedQueries[0] == "f_third" {
  185. isNeedToDealText = true
  186. }
  187. }
  188. stringlen := len(text)
  189. if isNeedToDealText && stringlen > 200 {
  190. index := strings.Index(text, Key)
  191. if index > 0 {
  192. start := index - 50
  193. if start < 0 {
  194. start = 0
  195. }
  196. end := index + 150
  197. if end >= stringlen {
  198. end = stringlen
  199. }
  200. return text[start:end]
  201. } else {
  202. return text[0:200]
  203. }
  204. } else {
  205. if stringlen > 200 {
  206. return text[0:200]
  207. } else {
  208. return text
  209. }
  210. }
  211. }
  212. func searchUserOrOrg(ctx *context.Context, TableName string, Key string, Page int, PageSize int, IsQueryUser bool) {
  213. /*
  214. 用户或者组织 ES名称: user-es-index
  215. 搜索:
  216. name , 名称
  217. full_name 全名
  218. description 描述或者简介
  219. 排序:
  220. created_unix
  221. 名称字母序
  222. */
  223. SortBy := ctx.Query("SortBy")
  224. if SortBy == "" {
  225. SortBy = "updated_unix.keyword"
  226. }
  227. ascending := ctx.QueryBool("Ascending")
  228. boolQ := elastic.NewBoolQuery()
  229. if Key != "" {
  230. nameQuery := elastic.NewMatchQuery("name", Key).Boost(2).QueryName("f_first")
  231. full_nameQuery := elastic.NewMatchQuery("full_name", Key).Boost(1.5).QueryName("f_second")
  232. descriptionQuery := elastic.NewMatchQuery("description", Key).Boost(1).QueryName("f_third")
  233. boolQ.Should(nameQuery, full_nameQuery, descriptionQuery)
  234. }
  235. typeValue := 1
  236. if IsQueryUser {
  237. typeValue = 0
  238. }
  239. UserOrOrgQuery := elastic.NewTermQuery("type", typeValue)
  240. boolQ.Must(UserOrOrgQuery)
  241. res, err := client.Search(TableName).Query(boolQ).Sort(SortBy, ascending).From((Page - 1) * PageSize).Size(PageSize).Do(ctx.Req.Context())
  242. if err == nil {
  243. result := makeUserOrOrgResult(res, Key, ctx)
  244. ctx.JSON(200, result)
  245. } else {
  246. log.Info("query es error," + err.Error())
  247. ctx.JSON(200, "")
  248. }
  249. }
  250. func makeUserOrOrgResult(sRes *elastic.SearchResult, Key string, ctx *context.Context) *SearchRes {
  251. total := sRes.Hits.TotalHits.Value
  252. result := make([]map[string]interface{}, 0)
  253. for i, hit := range sRes.Hits.Hits {
  254. log.Info("this is user query " + fmt.Sprint(i) + " result.")
  255. recordSource := make(map[string]interface{})
  256. source, err := hit.Source.MarshalJSON()
  257. if err == nil {
  258. err = json.Unmarshal(source, &recordSource)
  259. if err == nil {
  260. record := make(map[string]interface{})
  261. record["id"] = hit.Id
  262. record["name"] = recordSource["name"]
  263. record["full_name"] = recordSource["full_name"]
  264. if recordSource["description"] != nil {
  265. desc := recordSource["description"].(string)
  266. record["description"] = dealLongText(desc, Key, hit.MatchedQueries)
  267. } else {
  268. record["description"] = ""
  269. }
  270. if ctx.User != nil {
  271. record["email"] = recordSource["email"]
  272. } else {
  273. record["email"] = ""
  274. }
  275. record["location"] = recordSource["location"]
  276. record["website"] = recordSource["website"]
  277. record["num_repos"] = recordSource["num_repos"]
  278. record["num_teams"] = recordSource["num_teams"]
  279. record["num_members"] = recordSource["num_members"]
  280. record["avatar"] = strings.TrimRight(setting.AppSubURL, "/") + "/user/avatar/" + recordSource["name"].(string) + "/" + strconv.Itoa(-1)
  281. record["updated_unix"] = recordSource["updated_unix"]
  282. record["created_unix"] = recordSource["created_unix"]
  283. result = append(result, record)
  284. } else {
  285. log.Info("deal user source error," + err.Error())
  286. }
  287. } else {
  288. log.Info("deal user source error," + err.Error())
  289. }
  290. }
  291. returnObj := &SearchRes{
  292. Total: total,
  293. Result: result,
  294. }
  295. return returnObj
  296. }
  297. func searchDataSet(ctx *context.Context, TableName string, Key string, Page int, PageSize int) {
  298. /*
  299. 数据集,ES名称:dataset-es-index
  300. 搜索:
  301. title , 名称
  302. description 描述
  303. category 标签
  304. file_name 数据集文件名称
  305. 排序:
  306. download_times
  307. */
  308. SortBy := ctx.Query("SortBy")
  309. if SortBy == "" {
  310. SortBy = "download_times.keyword"
  311. }
  312. ascending := ctx.QueryBool("Ascending")
  313. log.Info("query searchRepo start")
  314. boolQ := elastic.NewBoolQuery()
  315. if Key != "" {
  316. nameQuery := elastic.NewMatchQuery("title", Key).Boost(2).QueryName("f_first")
  317. descQuery := elastic.NewMatchQuery("description", Key).Boost(1.5).QueryName("f_second")
  318. fileNameQuery := elastic.NewMatchQuery("file_name", Key).Boost(1).QueryName("f_third")
  319. categoryQuery := elastic.NewMatchQuery("category", Key).Boost(1).QueryName("f_fourth")
  320. boolQ.Should(nameQuery, descQuery, categoryQuery, fileNameQuery)
  321. res, err := client.Search(TableName).Query(boolQ).Sort(SortBy, ascending).From((Page - 1) * PageSize).Size(PageSize).Do(ctx.Req.Context())
  322. if err == nil {
  323. result := makeDatasetResult(res, Key)
  324. ctx.JSON(200, result)
  325. } else {
  326. log.Info("query es error," + err.Error())
  327. }
  328. } else {
  329. log.Info("query all content.")
  330. //搜索的属性要指定{"timestamp":{"unmapped_type":"date"}}
  331. res, err := client.Search(TableName).Sort(SortBy, ascending).From((Page - 1) * PageSize).Size(PageSize).Do(ctx.Req.Context())
  332. if err == nil {
  333. result := makeRepoResult(res, "")
  334. ctx.JSON(200, result)
  335. } else {
  336. log.Info("query es error," + err.Error())
  337. ctx.JSON(200, "")
  338. }
  339. }
  340. }
  341. func makeDatasetResult(sRes *elastic.SearchResult, Key string) *SearchRes {
  342. total := sRes.Hits.TotalHits.Value
  343. result := make([]map[string]interface{}, 0)
  344. for i, hit := range sRes.Hits.Hits {
  345. log.Info("this is dataset query " + fmt.Sprint(i) + " result.")
  346. recordSource := make(map[string]interface{})
  347. source, err := hit.Source.MarshalJSON()
  348. if err == nil {
  349. err = json.Unmarshal(source, &recordSource)
  350. if err == nil {
  351. record := make(map[string]interface{})
  352. record["id"] = hit.Id
  353. userId := recordSource["user_id"].(int64)
  354. user, errUser := models.GetUserByID(userId)
  355. if errUser == nil {
  356. record["owerName"] = user.GetDisplayName()
  357. record["avatar"] = user.RelAvatarLink()
  358. }
  359. record["title"] = recordSource["title"]
  360. record["category"] = recordSource["category"]
  361. if recordSource["description"] != nil {
  362. desc := recordSource["description"].(string)
  363. record["description"] = dealLongText(desc, Key, hit.MatchedQueries)
  364. } else {
  365. record["description"] = ""
  366. }
  367. record["download_times"] = recordSource["download_times"]
  368. record["created_unix"] = recordSource["created_unix"]
  369. result = append(result, record)
  370. } else {
  371. log.Info("deal dataset source error," + err.Error())
  372. }
  373. } else {
  374. log.Info("deal dataset source error," + err.Error())
  375. }
  376. }
  377. returnObj := &SearchRes{
  378. Total: total,
  379. Result: result,
  380. }
  381. return returnObj
  382. }
  383. func searchIssue(ctx *context.Context, TableName string, Key string, Page int, PageSize int) {
  384. /*
  385. 任务,合并请求 ES名称:issue-es-index
  386. 搜索:
  387. name character varying(255) , 标题
  388. content text, 内容
  389. comment text, 评论
  390. 排序:
  391. updated_unix
  392. */
  393. SortBy := ctx.Query("SortBy")
  394. if SortBy == "" {
  395. SortBy = "updated_unix.keyword"
  396. }
  397. ascending := ctx.QueryBool("Ascending")
  398. boolQ := elastic.NewBoolQuery()
  399. if Key != "" {
  400. log.Info("issue Key=" + Key)
  401. nameQuery := elastic.NewMatchQuery("name", Key).Boost(2).QueryName("f_first")
  402. contentQuery := elastic.NewMatchQuery("content", Key).Boost(1.5).QueryName("f_second")
  403. commentQuery := elastic.NewMatchQuery("comment", Key).Boost(1).QueryName("f_third")
  404. boolQ.Must(nameQuery, contentQuery, commentQuery)
  405. }
  406. isIssueQuery := elastic.NewTermQuery("is_pull", "f")
  407. boolQ.Must(isIssueQuery)
  408. res, err := client.Search(TableName).Query(boolQ).Sort(SortBy, ascending).From((Page - 1) * PageSize).Size(PageSize).Do(ctx.Req.Context())
  409. if err == nil {
  410. searchJson, _ := json.Marshal(res)
  411. log.Info("searchJson=" + string(searchJson))
  412. result := makeIssueResult(res, Key)
  413. ctx.JSON(200, result)
  414. } else {
  415. log.Info("query es error," + err.Error())
  416. }
  417. }
  418. func makeIssueResult(sRes *elastic.SearchResult, Key string) *SearchRes {
  419. total := sRes.Hits.TotalHits.Value
  420. result := make([]map[string]interface{}, 0)
  421. for i, hit := range sRes.Hits.Hits {
  422. log.Info("this is issue query " + fmt.Sprint(i) + " result.")
  423. recordSource := make(map[string]interface{})
  424. source, err := hit.Source.MarshalJSON()
  425. if err == nil {
  426. err = json.Unmarshal(source, &recordSource)
  427. if err == nil {
  428. record := make(map[string]interface{})
  429. record["id"] = hit.Id
  430. record["repo_id"] = recordSource["repo_id"]
  431. log.Info("recordSource[\"repo_id\"]=" + fmt.Sprint(recordSource["repo_id"]))
  432. repoIdstr := recordSource["repo_id"].(string)
  433. repoId, cerr := strconv.ParseInt(repoIdstr, 10, 64)
  434. if cerr == nil {
  435. repo, errRepo := models.GetRepositoryByID(repoId)
  436. if errRepo == nil {
  437. record["repoUrl"] = repo.FullName()
  438. record["avatar"] = repo.RelAvatarLink()
  439. }
  440. }
  441. record["name"] = recordSource["name"]
  442. if recordSource["content"] != nil {
  443. desc := recordSource["content"].(string)
  444. record["content"] = dealLongText(desc, Key, hit.MatchedQueries)
  445. } else {
  446. record["content"] = ""
  447. }
  448. if recordSource["pr_id"] != nil {
  449. record["pr_id"] = recordSource["pr_id"]
  450. }
  451. if recordSource["comment"] != nil {
  452. desc := recordSource["comment"].(string)
  453. record["comment"] = dealLongText(desc, Key, hit.MatchedQueries)
  454. } else {
  455. record["comment"] = ""
  456. }
  457. record["num_comments"] = recordSource["num_comments"]
  458. record["updated_unix"] = recordSource["updated_unix"]
  459. result = append(result, record)
  460. } else {
  461. log.Info("deal issue source error," + err.Error())
  462. }
  463. } else {
  464. log.Info("deal issue source error," + err.Error())
  465. }
  466. }
  467. returnObj := &SearchRes{
  468. Total: total,
  469. Result: result,
  470. }
  471. return returnObj
  472. }
  473. func searchPR(ctx *context.Context, TableName string, Key string, Page int, PageSize int) {
  474. /*
  475. 任务,合并请求 ES名称:issue-es-index
  476. 搜索:
  477. name character varying(255) , 标题
  478. content text, 内容
  479. comment text, 评论
  480. 排序:
  481. updated_unix
  482. */
  483. SortBy := ctx.Query("SortBy")
  484. if SortBy == "" {
  485. SortBy = "updated_unix.keyword"
  486. }
  487. ascending := ctx.QueryBool("Ascending")
  488. boolQ := elastic.NewBoolQuery()
  489. if Key != "" {
  490. nameQuery := elastic.NewMatchQuery("name", Key).Boost(1024).QueryName("f_first")
  491. contentQuery := elastic.NewMatchQuery("content", Key).Boost(1.5).QueryName("f_second")
  492. commentQuery := elastic.NewMatchQuery("comment", Key).Boost(1).QueryName("f_third")
  493. boolQ.Should(nameQuery, contentQuery, commentQuery)
  494. }
  495. isIssueQuery := elastic.NewTermQuery("is_pull", "t")
  496. boolQ.Must(isIssueQuery)
  497. res, err := client.Search(TableName).Query(boolQ).Sort(SortBy, ascending).From((Page - 1) * PageSize).Size(PageSize).Do(ctx.Req.Context())
  498. if err == nil {
  499. result := makeIssueResult(res, Key)
  500. ctx.JSON(200, result)
  501. } else {
  502. log.Info("query es error," + err.Error())
  503. }
  504. }