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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890
  1. package routers
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "path/filepath"
  6. "strconv"
  7. "strings"
  8. "code.gitea.io/gitea/models"
  9. "code.gitea.io/gitea/modules/context"
  10. "code.gitea.io/gitea/modules/log"
  11. "code.gitea.io/gitea/modules/setting"
  12. "github.com/olivere/elastic/v7"
  13. )
  14. type SearchRes struct {
  15. Total int64
  16. Result []map[string]interface{}
  17. PrivateTotal int64
  18. }
  19. var client *elastic.Client
  20. func InitESClient() {
  21. ESSearchUrl := setting.ESSearchURL
  22. var err error
  23. client, err = elastic.NewClient(elastic.SetSniff(false), elastic.SetURL(ESSearchUrl))
  24. if err != nil {
  25. log.Info("es init error.")
  26. //panic(err)
  27. }
  28. }
  29. func Search(ctx *context.Context) {
  30. keyword := strings.Trim(ctx.Query("q"), " ")
  31. ctx.Data["Keyword"] = keyword
  32. ctx.Data["SortType"] = "newest"
  33. ctx.HTML(200, "explore/search_new")
  34. }
  35. func SearchApi(ctx *context.Context) {
  36. TableName := ctx.Query("TableName")
  37. Key := ctx.Query("Key")
  38. Page := ctx.QueryInt("Page")
  39. PageSize := ctx.QueryInt("PageSize")
  40. OnlyReturnNum := ctx.QueryBool("OnlyReturnNum")
  41. OnlySearchLabel := ctx.QueryBool("OnlySearchLabel")
  42. if Page <= 0 {
  43. Page = 1
  44. }
  45. if PageSize <= 0 || PageSize > 200 {
  46. PageSize = setting.UI.IssuePagingNum
  47. }
  48. if Key != "" && !OnlyReturnNum {
  49. models.SaveSearchKeywordToDb(Key)
  50. }
  51. if TableName == "repository" {
  52. if OnlySearchLabel {
  53. searchRepoByLabel(ctx, Key, Page, PageSize)
  54. } else {
  55. searchRepo(ctx, "repository-es-index", Key, Page, PageSize, OnlyReturnNum)
  56. }
  57. return
  58. } else if TableName == "issue" {
  59. searchIssue(ctx, "issue-es-index", Key, Page, PageSize, OnlyReturnNum)
  60. return
  61. } else if TableName == "user" {
  62. searchUserOrOrg(ctx, "user-es-index", Key, Page, PageSize, true, OnlyReturnNum)
  63. return
  64. } else if TableName == "org" {
  65. searchUserOrOrg(ctx, "user-es-index", Key, Page, PageSize, false, OnlyReturnNum)
  66. return
  67. } else if TableName == "dataset" {
  68. searchDataSet(ctx, "dataset-es-index", Key, Page, PageSize, OnlyReturnNum)
  69. return
  70. } else if TableName == "pr" {
  71. searchPR(ctx, "issue-es-index", Key, Page, PageSize, OnlyReturnNum)
  72. return
  73. }
  74. }
  75. func searchRepoByLabel(ctx *context.Context, Key string, Page int, PageSize int) {
  76. /*
  77. 项目, ES名称: repository-es-index
  78. 搜索:
  79. name character varying(255) , 项目名称
  80. description text, 项目描述
  81. topics json, 标签
  82. 排序:
  83. updated_unix
  84. num_watches,
  85. num_stars,
  86. num_forks,
  87. */
  88. SortBy := ctx.Query("SortBy")
  89. if SortBy == "" {
  90. SortBy = "updated_unix.keyword"
  91. }
  92. PrivateTotal := ctx.QueryInt("PrivateTotal")
  93. WebTotal := ctx.QueryInt("WebTotal")
  94. ascending := ctx.QueryBool("Ascending")
  95. from := (Page - 1) * PageSize
  96. resultObj := &SearchRes{}
  97. log.Info("WebTotal=" + fmt.Sprint(WebTotal))
  98. log.Info("PrivateTotal=" + fmt.Sprint(PrivateTotal))
  99. resultObj.Result = make([]map[string]interface{}, 0)
  100. if from < PrivateTotal || from == 0 {
  101. orderBy := models.SearchOrderByRecentUpdated
  102. switch SortBy {
  103. case "updated_unix.keyword":
  104. orderBy = models.SearchOrderByRecentUpdated
  105. case "num_stars":
  106. orderBy = models.SearchOrderByStarsReverse
  107. case "num_forks":
  108. orderBy = models.SearchOrderByForksReverse
  109. case "num_watches":
  110. orderBy = models.SearchOrderByWatches
  111. }
  112. log.Info("actor is null?:" + fmt.Sprint(ctx.User == nil))
  113. repos, count, err := models.SearchRepository(&models.SearchRepoOptions{
  114. ListOptions: models.ListOptions{
  115. Page: Page,
  116. PageSize: PageSize,
  117. },
  118. Actor: ctx.User,
  119. OrderBy: orderBy,
  120. Private: true,
  121. OnlyPrivate: true,
  122. TopicOnly: true,
  123. TopicName: Key,
  124. IncludeDescription: setting.UI.SearchRepoDescription,
  125. })
  126. if err != nil {
  127. ctx.JSON(200, "")
  128. return
  129. }
  130. resultObj.PrivateTotal = count
  131. if repos.Len() > 0 {
  132. log.Info("Query private repo number is:" + fmt.Sprint(repos.Len()))
  133. makePrivateRepo(repos, resultObj, Key)
  134. } else {
  135. log.Info("not found private repo,keyword=" + Key)
  136. }
  137. if repos.Len() >= PageSize {
  138. resultObj.Total = int64(WebTotal)
  139. ctx.JSON(200, resultObj)
  140. return
  141. }
  142. } else {
  143. resultObj.PrivateTotal = int64(PrivateTotal)
  144. }
  145. from = from - PrivateTotal
  146. if from < 0 {
  147. from = 0
  148. }
  149. Size := PageSize - len(resultObj.Result)
  150. log.Info("query searchRepoByLabel start")
  151. if Key != "" {
  152. boolQ := elastic.NewBoolQuery()
  153. topicsQuery := elastic.NewMatchQuery("topics", Key)
  154. boolQ.Should(topicsQuery)
  155. res, err := client.Search("repository-es-index").Query(boolQ).SortBy(elastic.NewScoreSort(), elastic.NewFieldSort(SortBy).Order(ascending)).From(from).Size(Size).Highlight(queryHighlight("topics")).Do(ctx.Req.Context())
  156. if err == nil {
  157. searchJson, _ := json.Marshal(res)
  158. log.Info("searchJson=" + string(searchJson))
  159. esresult := makeRepoResult(res, "", false)
  160. resultObj.Total = resultObj.PrivateTotal + esresult.Total
  161. resultObj.Result = append(resultObj.Result, esresult.Result...)
  162. ctx.JSON(200, esresult)
  163. } else {
  164. log.Info("query es error," + err.Error())
  165. ctx.JSON(200, "")
  166. }
  167. } else {
  168. ctx.JSON(200, "")
  169. }
  170. }
  171. func searchRepo(ctx *context.Context, TableName string, Key string, Page int, PageSize int, OnlyReturnNum bool) {
  172. /*
  173. 项目, ES名称: repository-es-index
  174. 搜索:
  175. name character varying(255) , 项目名称
  176. description text, 项目描述
  177. topics json, 标签
  178. 排序:
  179. updated_unix
  180. num_watches,
  181. num_stars,
  182. num_forks,
  183. */
  184. SortBy := ctx.Query("SortBy")
  185. if SortBy == "" {
  186. SortBy = "updated_unix.keyword"
  187. }
  188. PrivateTotal := ctx.QueryInt("PrivateTotal")
  189. WebTotal := ctx.QueryInt("WebTotal")
  190. ascending := ctx.QueryBool("Ascending")
  191. from := (Page - 1) * PageSize
  192. resultObj := &SearchRes{}
  193. log.Info("WebTotal=" + fmt.Sprint(WebTotal))
  194. log.Info("PrivateTotal=" + fmt.Sprint(PrivateTotal))
  195. resultObj.Result = make([]map[string]interface{}, 0)
  196. if from < PrivateTotal || from == 0 {
  197. orderBy := models.SearchOrderByRecentUpdated
  198. switch SortBy {
  199. case "updated_unix.keyword":
  200. orderBy = models.SearchOrderByRecentUpdated
  201. case "num_stars":
  202. orderBy = models.SearchOrderByStarsReverse
  203. case "num_forks":
  204. orderBy = models.SearchOrderByForksReverse
  205. case "num_watches":
  206. orderBy = models.SearchOrderByWatches
  207. }
  208. log.Info("actor is null?:" + fmt.Sprint(ctx.User == nil))
  209. repos, count, err := models.SearchRepository(&models.SearchRepoOptions{
  210. ListOptions: models.ListOptions{
  211. Page: Page,
  212. PageSize: PageSize,
  213. },
  214. Actor: ctx.User,
  215. OrderBy: orderBy,
  216. Private: true,
  217. OnlyPrivate: true,
  218. Keyword: Key,
  219. IncludeDescription: setting.UI.SearchRepoDescription,
  220. })
  221. if err != nil {
  222. ctx.JSON(200, "")
  223. return
  224. }
  225. resultObj.PrivateTotal = count
  226. if repos.Len() > 0 {
  227. log.Info("Query private repo number is:" + fmt.Sprint(repos.Len()))
  228. makePrivateRepo(repos, resultObj, Key)
  229. } else {
  230. log.Info("not found private repo,keyword=" + Key)
  231. }
  232. if repos.Len() >= PageSize {
  233. resultObj.Total = int64(WebTotal)
  234. ctx.JSON(200, resultObj)
  235. return
  236. }
  237. } else {
  238. resultObj.PrivateTotal = int64(PrivateTotal)
  239. }
  240. from = from - PrivateTotal
  241. if from < 0 {
  242. from = 0
  243. }
  244. Size := PageSize - len(resultObj.Result)
  245. log.Info("query searchRepo start")
  246. if Key != "" {
  247. boolQ := elastic.NewBoolQuery()
  248. nameQuery := elastic.NewMatchQuery("alias", Key).Boost(1024).QueryName("f_first")
  249. descriptionQuery := elastic.NewMatchQuery("description", Key).Boost(1.5).QueryName("f_second")
  250. topicsQuery := elastic.NewMatchQuery("topics", Key).Boost(1).QueryName("f_third")
  251. boolQ.Should(nameQuery, descriptionQuery, topicsQuery)
  252. res, err := client.Search(TableName).Query(boolQ).SortBy(elastic.NewScoreSort(), elastic.NewFieldSort(SortBy).Order(ascending)).From(from).Size(Size).Highlight(queryHighlight("alias", "description", "topics")).Do(ctx.Req.Context())
  253. if err == nil {
  254. searchJson, _ := json.Marshal(res)
  255. log.Info("searchJson=" + string(searchJson))
  256. esresult := makeRepoResult(res, Key, OnlyReturnNum)
  257. resultObj.Total = resultObj.PrivateTotal + esresult.Total
  258. resultObj.Result = append(resultObj.Result, esresult.Result...)
  259. ctx.JSON(200, resultObj)
  260. } else {
  261. log.Info("query es error," + err.Error())
  262. ctx.JSON(200, "")
  263. }
  264. } else {
  265. log.Info("query all content.")
  266. //搜索的属性要指定{"timestamp":{"unmapped_type":"date"}}
  267. res, err := client.Search(TableName).SortBy(elastic.NewFieldSort(SortBy).Order(ascending)).From(from).Size(Size).Do(ctx.Req.Context())
  268. if err == nil {
  269. searchJson, _ := json.Marshal(res)
  270. log.Info("searchJson=" + string(searchJson))
  271. esresult := makeRepoResult(res, "", OnlyReturnNum)
  272. resultObj.Total = resultObj.PrivateTotal + esresult.Total
  273. resultObj.Result = append(resultObj.Result, esresult.Result...)
  274. ctx.JSON(200, resultObj)
  275. } else {
  276. log.Info("query es error," + err.Error())
  277. ctx.JSON(200, "")
  278. }
  279. }
  280. }
  281. func makePrivateRepo(repos models.RepositoryList, res *SearchRes, keyword string) {
  282. for _, repo := range repos {
  283. record := make(map[string]interface{})
  284. record["id"] = repo.ID
  285. record["name"] = makeHighLight(keyword, repo.Name)
  286. record["real_name"] = repo.Name
  287. record["owner_name"] = repo.OwnerName
  288. record["description"] = truncLongText(makeHighLight(keyword, repo.Description), true)
  289. hightTopics := make([]string, 0)
  290. if len(repo.Topics) > 0 {
  291. for _, t := range repo.Topics {
  292. hightTopics = append(hightTopics, makeHighLight(keyword, t))
  293. }
  294. }
  295. record["hightTopics"] = hightTopics
  296. record["num_watches"] = repo.NumWatches
  297. record["num_stars"] = repo.NumStars
  298. record["num_forks"] = repo.NumForks
  299. record["alias"] = truncLongText(makeHighLight(keyword, repo.Alias), true)
  300. record["lower_alias"] = repo.LowerAlias
  301. record["topics"] = repo.Topics
  302. record["avatar"] = repo.RelAvatarLink()
  303. if len(repo.RelAvatarLink()) == 0 {
  304. record["avatar"] = setting.RepositoryAvatarFallbackImage
  305. }
  306. record["updated_unix"] = repo.UpdatedUnix
  307. lang, err := repo.GetTopLanguageStats(1)
  308. if err == nil && len(lang) > 0 {
  309. record["lang"] = lang[0].Language
  310. } else {
  311. record["lang"] = ""
  312. }
  313. record["is_private"] = true
  314. res.Result = append(res.Result, record)
  315. }
  316. }
  317. func makeHighLight(keyword string, dest string) string {
  318. textRune := []rune(dest)
  319. index := findFont(textRune, 0, []rune(keyword))
  320. if index > 0 {
  321. dest = strings.ReplaceAll(dest, keyword, "\u003cfont color='red'\u003e"+keyword+"\u003c/font\u003e")
  322. }
  323. return dest
  324. }
  325. func makeRepoResult(sRes *elastic.SearchResult, Key string, OnlyReturnNum bool) *SearchRes {
  326. total := sRes.Hits.TotalHits.Value
  327. result := make([]map[string]interface{}, 0)
  328. if !OnlyReturnNum {
  329. for i, hit := range sRes.Hits.Hits {
  330. log.Info("this is repo query " + fmt.Sprint(i) + " result.")
  331. recordSource := make(map[string]interface{})
  332. source, err := hit.Source.MarshalJSON()
  333. if err == nil {
  334. err = json.Unmarshal(source, &recordSource)
  335. if err == nil {
  336. record := make(map[string]interface{})
  337. record["id"] = hit.Id
  338. record["alias"] = getLabelValue("alias", recordSource, hit.Highlight)
  339. record["real_name"] = recordSource["name"]
  340. record["owner_name"] = recordSource["owner_name"]
  341. if recordSource["description"] != nil {
  342. desc := getLabelValue("description", recordSource, hit.Highlight)
  343. record["description"] = dealLongText(desc, Key, hit.MatchedQueries)
  344. } else {
  345. record["description"] = ""
  346. }
  347. record["hightTopics"] = jsonStrToArray(getLabelValue("topics", recordSource, hit.Highlight))
  348. record["num_watches"] = recordSource["num_watches"]
  349. record["num_stars"] = recordSource["num_stars"]
  350. record["num_forks"] = recordSource["num_forks"]
  351. record["lower_alias"] = recordSource["lower_alias"]
  352. if recordSource["topics"] != nil {
  353. topicsStr := recordSource["topics"].(string)
  354. log.Info("topicsStr=" + topicsStr)
  355. if topicsStr != "null" {
  356. record["topics"] = jsonStrToArray(topicsStr)
  357. }
  358. }
  359. if recordSource["avatar"] != nil {
  360. avatarstr := recordSource["avatar"].(string)
  361. if len(avatarstr) == 0 {
  362. record["avatar"] = setting.RepositoryAvatarFallbackImage
  363. } else {
  364. record["avatar"] = filepath.Join(setting.RepositoryAvatarUploadPath, avatarstr)
  365. }
  366. }
  367. record["updated_unix"] = recordSource["updated_unix"]
  368. record["lang"] = recordSource["lang"]
  369. record["is_private"] = false
  370. result = append(result, record)
  371. } else {
  372. log.Info("deal repo source error," + err.Error())
  373. }
  374. } else {
  375. log.Info("deal repo source error," + err.Error())
  376. }
  377. }
  378. }
  379. returnObj := &SearchRes{
  380. Total: total,
  381. Result: result,
  382. }
  383. return returnObj
  384. }
  385. func jsonStrToArray(str string) []string {
  386. b := []byte(str)
  387. strs := make([]string, 0)
  388. err := json.Unmarshal(b, &strs)
  389. if err != nil {
  390. log.Info("convert str arrar error, str=" + str)
  391. }
  392. return strs
  393. }
  394. func dealLongText(text string, Key string, MatchedQueries []string) string {
  395. var isNeedToDealText bool
  396. isNeedToDealText = false
  397. if len(MatchedQueries) > 0 && Key != "" {
  398. if MatchedQueries[0] == "f_second" || MatchedQueries[0] == "f_third" {
  399. isNeedToDealText = true
  400. }
  401. }
  402. return truncLongText(text, isNeedToDealText)
  403. }
  404. func truncLongText(text string, isNeedToDealText bool) string {
  405. startStr := "color="
  406. textRune := []rune(text)
  407. stringlen := len(textRune)
  408. if isNeedToDealText && stringlen > 200 {
  409. index := findFont(textRune, 0, []rune(startStr))
  410. if index > 0 {
  411. start := index - 50
  412. if start < 0 {
  413. start = 0
  414. }
  415. end := index + 150
  416. if end >= stringlen {
  417. end = stringlen
  418. }
  419. return string(trimFontHtml(textRune[start:end])) + "..."
  420. } else {
  421. return string(trimFontHtml(textRune[0:200])) + "..."
  422. }
  423. } else {
  424. if stringlen > 200 {
  425. return string(trimFontHtml(textRune[0:200])) + "..."
  426. } else {
  427. return text
  428. }
  429. }
  430. }
  431. func trimFontHtml(text []rune) []rune {
  432. startRune := rune('<')
  433. endRune := rune('>')
  434. for i := 0; i < len(text); i++ {
  435. if text[i] == startRune { //start <
  436. re := false
  437. j := i + 1
  438. for ; j < len(text); j++ {
  439. if text[j] == endRune {
  440. re = true
  441. break
  442. }
  443. }
  444. if re { //found >
  445. i = j
  446. } else {
  447. return text[0:i]
  448. }
  449. }
  450. }
  451. return text
  452. }
  453. func findFont(text []rune, startindex int, childText []rune) int {
  454. for i := 0; i < len(text); i++ {
  455. if text[i] == childText[0] {
  456. re := true
  457. for j, k := range childText {
  458. if k != text[i+j] {
  459. re = false
  460. break
  461. }
  462. }
  463. if re {
  464. return i
  465. }
  466. }
  467. }
  468. return -1
  469. }
  470. func searchUserOrOrg(ctx *context.Context, TableName string, Key string, Page int, PageSize int, IsQueryUser bool, OnlyReturnNum bool) {
  471. /*
  472. 用户或者组织 ES名称: user-es-index
  473. 搜索:
  474. name , 名称
  475. full_name 全名
  476. description 描述或者简介
  477. 排序:
  478. created_unix
  479. 名称字母序
  480. */
  481. SortBy := ctx.Query("SortBy")
  482. if SortBy == "" {
  483. SortBy = "created_unix.keyword"
  484. }
  485. ascending := ctx.QueryBool("Ascending")
  486. boolQ := elastic.NewBoolQuery()
  487. typeValue := 1
  488. if IsQueryUser {
  489. typeValue = 0
  490. }
  491. UserOrOrgQuery := elastic.NewTermQuery("type", typeValue)
  492. if Key != "" {
  493. boolKeyQ := elastic.NewBoolQuery()
  494. log.Info("user or org Key=" + Key)
  495. nameQuery := elastic.NewMatchQuery("name", Key).Boost(2).QueryName("f_first")
  496. full_nameQuery := elastic.NewMatchQuery("full_name", Key).Boost(1.5).QueryName("f_second")
  497. descriptionQuery := elastic.NewMatchQuery("description", Key).Boost(1).QueryName("f_third")
  498. boolKeyQ.Should(nameQuery, full_nameQuery, descriptionQuery)
  499. boolQ.Must(UserOrOrgQuery, boolKeyQ)
  500. } else {
  501. boolQ.Must(UserOrOrgQuery)
  502. }
  503. res, err := client.Search(TableName).Query(boolQ).Sort(SortBy, ascending).From((Page - 1) * PageSize).Size(PageSize).Highlight(queryHighlight("name", "full_name", "description")).Do(ctx.Req.Context())
  504. if err == nil {
  505. searchJson, _ := json.Marshal(res)
  506. log.Info("searchJson=" + string(searchJson))
  507. result := makeUserOrOrgResult(res, Key, ctx, OnlyReturnNum)
  508. ctx.JSON(200, result)
  509. } else {
  510. log.Info("query es error," + err.Error())
  511. ctx.JSON(200, "")
  512. }
  513. }
  514. func getLabelValue(key string, recordSource map[string]interface{}, searchHighliht elastic.SearchHitHighlight) string {
  515. if value, ok := searchHighliht[key]; !ok {
  516. if recordSource[key] != nil {
  517. return recordSource[key].(string)
  518. } else {
  519. return ""
  520. }
  521. } else {
  522. return value[0]
  523. }
  524. }
  525. func makeUserOrOrgResult(sRes *elastic.SearchResult, Key string, ctx *context.Context, OnlyReturnNum bool) *SearchRes {
  526. total := sRes.Hits.TotalHits.Value
  527. result := make([]map[string]interface{}, 0)
  528. if !OnlyReturnNum {
  529. for i, hit := range sRes.Hits.Hits {
  530. log.Info("this is user query " + fmt.Sprint(i) + " result.")
  531. recordSource := make(map[string]interface{})
  532. source, err := hit.Source.MarshalJSON()
  533. if err == nil {
  534. err = json.Unmarshal(source, &recordSource)
  535. if err == nil {
  536. record := make(map[string]interface{})
  537. record["id"] = hit.Id
  538. record["name"] = getLabelValue("name", recordSource, hit.Highlight)
  539. record["real_name"] = recordSource["name"]
  540. record["full_name"] = getLabelValue("full_name", recordSource, hit.Highlight)
  541. if recordSource["description"] != nil {
  542. desc := getLabelValue("description", recordSource, hit.Highlight)
  543. record["description"] = dealLongText(desc, Key, hit.MatchedQueries)
  544. } else {
  545. record["description"] = ""
  546. }
  547. if ctx.User != nil {
  548. record["email"] = recordSource["email"]
  549. } else {
  550. record["email"] = ""
  551. }
  552. record["location"] = recordSource["location"]
  553. record["website"] = recordSource["website"]
  554. record["num_repos"] = recordSource["num_repos"]
  555. record["num_teams"] = recordSource["num_teams"]
  556. record["num_members"] = recordSource["num_members"]
  557. record["avatar"] = strings.TrimRight(setting.AppSubURL, "/") + "/user/avatar/" + recordSource["name"].(string) + "/" + strconv.Itoa(-1)
  558. record["updated_unix"] = recordSource["updated_unix"]
  559. record["created_unix"] = recordSource["created_unix"]
  560. result = append(result, record)
  561. } else {
  562. log.Info("deal user source error," + err.Error())
  563. }
  564. } else {
  565. log.Info("deal user source error," + err.Error())
  566. }
  567. }
  568. }
  569. returnObj := &SearchRes{
  570. Total: total,
  571. Result: result,
  572. }
  573. return returnObj
  574. }
  575. func searchDataSet(ctx *context.Context, TableName string, Key string, Page int, PageSize int, OnlyReturnNum bool) {
  576. /*
  577. 数据集,ES名称:dataset-es-index
  578. 搜索:
  579. title , 名称
  580. description 描述
  581. category 标签
  582. file_name 数据集文件名称
  583. 排序:
  584. download_times
  585. */
  586. SortBy := ctx.Query("SortBy")
  587. if SortBy == "" {
  588. SortBy = "download_times.keyword"
  589. }
  590. ascending := ctx.QueryBool("Ascending")
  591. log.Info("query searchRepo start")
  592. boolQ := elastic.NewBoolQuery()
  593. if Key != "" {
  594. nameQuery := elastic.NewMatchQuery("title", Key).Boost(2).QueryName("f_first")
  595. descQuery := elastic.NewMatchQuery("description", Key).Boost(1.5).QueryName("f_second")
  596. fileNameQuery := elastic.NewMatchQuery("file_name", Key).Boost(1).QueryName("f_third")
  597. categoryQuery := elastic.NewMatchQuery("category", Key).Boost(1).QueryName("f_fourth")
  598. boolQ.Should(nameQuery, descQuery, categoryQuery, fileNameQuery)
  599. res, err := client.Search(TableName).Query(boolQ).Sort(SortBy, ascending).From((Page - 1) * PageSize).Size(PageSize).Highlight(queryHighlight("title", "description", "file_name", "category")).Do(ctx.Req.Context())
  600. if err == nil {
  601. searchJson, _ := json.Marshal(res)
  602. log.Info("searchJson=" + string(searchJson))
  603. result := makeDatasetResult(res, Key, OnlyReturnNum)
  604. ctx.JSON(200, result)
  605. } else {
  606. log.Info("query es error," + err.Error())
  607. }
  608. } else {
  609. log.Info("query all content.")
  610. //搜索的属性要指定{"timestamp":{"unmapped_type":"date"}}
  611. res, err := client.Search(TableName).Sort(SortBy, ascending).From((Page - 1) * PageSize).Size(PageSize).Do(ctx.Req.Context())
  612. if err == nil {
  613. searchJson, _ := json.Marshal(res)
  614. log.Info("searchJson=" + string(searchJson))
  615. result := makeDatasetResult(res, "", OnlyReturnNum)
  616. ctx.JSON(200, result)
  617. } else {
  618. log.Info("query es error," + err.Error())
  619. ctx.JSON(200, "")
  620. }
  621. }
  622. }
  623. func makeDatasetResult(sRes *elastic.SearchResult, Key string, OnlyReturnNum bool) *SearchRes {
  624. total := sRes.Hits.TotalHits.Value
  625. result := make([]map[string]interface{}, 0)
  626. if !OnlyReturnNum {
  627. for i, hit := range sRes.Hits.Hits {
  628. log.Info("this is dataset query " + fmt.Sprint(i) + " result.")
  629. recordSource := make(map[string]interface{})
  630. source, err := hit.Source.MarshalJSON()
  631. if err == nil {
  632. err = json.Unmarshal(source, &recordSource)
  633. if err == nil {
  634. record := make(map[string]interface{})
  635. record["id"] = hit.Id
  636. userIdStr := recordSource["user_id"].(string)
  637. userId, cerr := strconv.ParseInt(userIdStr, 10, 64)
  638. if cerr == nil {
  639. user, errUser := models.GetUserByID(userId)
  640. if errUser == nil {
  641. record["owerName"] = user.GetDisplayName()
  642. record["avatar"] = user.RelAvatarLink()
  643. }
  644. }
  645. setRepoInfo(recordSource, record)
  646. record["title"] = getLabelValue("title", recordSource, hit.Highlight)
  647. record["category"] = getLabelValue("category", recordSource, hit.Highlight)
  648. if recordSource["description"] != nil {
  649. desc := getLabelValue("description", recordSource, hit.Highlight)
  650. record["description"] = dealLongText(desc, Key, hit.MatchedQueries)
  651. } else {
  652. record["description"] = ""
  653. }
  654. record["file_name"] = getLabelValue("file_name", recordSource, hit.Highlight)
  655. record["task"] = recordSource["task"]
  656. record["download_times"] = recordSource["download_times"]
  657. record["created_unix"] = recordSource["created_unix"]
  658. result = append(result, record)
  659. } else {
  660. log.Info("deal dataset source error," + err.Error())
  661. }
  662. } else {
  663. log.Info("deal dataset source error," + err.Error())
  664. }
  665. }
  666. }
  667. returnObj := &SearchRes{
  668. Total: total,
  669. Result: result,
  670. }
  671. return returnObj
  672. }
  673. func searchIssue(ctx *context.Context, TableName string, Key string, Page int, PageSize int, OnlyReturnNum bool) {
  674. /*
  675. 任务,合并请求 ES名称:issue-es-index
  676. 搜索:
  677. name character varying(255) , 标题
  678. content text, 内容
  679. comment text, 评论
  680. 排序:
  681. updated_unix
  682. */
  683. SortBy := ctx.Query("SortBy")
  684. if SortBy == "" {
  685. SortBy = "updated_unix.keyword"
  686. }
  687. ascending := ctx.QueryBool("Ascending")
  688. boolQ := elastic.NewBoolQuery()
  689. isIssueQuery := elastic.NewTermQuery("is_pull", "f")
  690. if Key != "" {
  691. boolKeyQ := elastic.NewBoolQuery()
  692. log.Info("issue Key=" + Key)
  693. nameQuery := elastic.NewMatchQuery("name", Key).Boost(2).QueryName("f_first")
  694. contentQuery := elastic.NewMatchQuery("content", Key).Boost(1.5).QueryName("f_second")
  695. commentQuery := elastic.NewMatchQuery("comment", Key).Boost(1).QueryName("f_third")
  696. boolKeyQ.Should(nameQuery, contentQuery, commentQuery)
  697. boolQ.Must(isIssueQuery, boolKeyQ)
  698. } else {
  699. boolQ.Must(isIssueQuery)
  700. }
  701. res, err := client.Search(TableName).Query(boolQ).Sort(SortBy, ascending).From((Page - 1) * PageSize).Size(PageSize).Highlight(queryHighlight("name", "content", "comment")).Do(ctx.Req.Context())
  702. if err == nil {
  703. searchJson, _ := json.Marshal(res)
  704. log.Info("searchJson=" + string(searchJson))
  705. result := makeIssueResult(res, Key, OnlyReturnNum)
  706. ctx.JSON(200, result)
  707. } else {
  708. log.Info("query es error," + err.Error())
  709. }
  710. }
  711. func queryHighlight(names ...string) *elastic.Highlight {
  712. re := elastic.NewHighlight()
  713. for i := 0; i < len(names); i++ {
  714. field := &elastic.HighlighterField{
  715. Name: names[i],
  716. }
  717. re.Fields(field)
  718. }
  719. re.PreTags("<font color='red'>")
  720. re.PostTags("</font>")
  721. return re
  722. }
  723. func setRepoInfo(recordSource map[string]interface{}, record map[string]interface{}) {
  724. repoIdstr := recordSource["repo_id"].(string)
  725. repoId, cerr := strconv.ParseInt(repoIdstr, 10, 64)
  726. if cerr == nil {
  727. repo, errRepo := models.GetRepositoryByID(repoId)
  728. if errRepo == nil {
  729. log.Info("repo_url=" + repo.FullName())
  730. record["repoUrl"] = repo.FullName()
  731. record["avatar"] = repo.RelAvatarLink()
  732. } else {
  733. log.Info("repo err=" + errRepo.Error())
  734. }
  735. } else {
  736. log.Info("parse int err=" + cerr.Error())
  737. }
  738. }
  739. func makeIssueResult(sRes *elastic.SearchResult, Key string, OnlyReturnNum bool) *SearchRes {
  740. total := sRes.Hits.TotalHits.Value
  741. result := make([]map[string]interface{}, 0)
  742. if !OnlyReturnNum {
  743. for i, hit := range sRes.Hits.Hits {
  744. log.Info("this is issue query " + fmt.Sprint(i) + " result.")
  745. recordSource := make(map[string]interface{})
  746. source, err := hit.Source.MarshalJSON()
  747. if err == nil {
  748. err = json.Unmarshal(source, &recordSource)
  749. if err == nil {
  750. record := make(map[string]interface{})
  751. record["id"] = hit.Id
  752. record["repo_id"] = recordSource["repo_id"]
  753. log.Info("recordSource[\"repo_id\"]=" + fmt.Sprint(recordSource["repo_id"]))
  754. setRepoInfo(recordSource, record)
  755. record["name"] = getLabelValue("name", recordSource, hit.Highlight)
  756. if recordSource["content"] != nil {
  757. desc := getLabelValue("content", recordSource, hit.Highlight)
  758. record["content"] = dealLongText(desc, Key, hit.MatchedQueries)
  759. if _, ok := hit.Highlight["content"]; !ok {
  760. if _, ok_comment := hit.Highlight["comment"]; ok_comment {
  761. desc := getLabelValue("comment", recordSource, hit.Highlight)
  762. record["content"] = dealLongText(desc, Key, hit.MatchedQueries)
  763. }
  764. }
  765. } else {
  766. if recordSource["comment"] != nil {
  767. desc := getLabelValue("comment", recordSource, hit.Highlight)
  768. record["content"] = dealLongText(desc, Key, hit.MatchedQueries)
  769. }
  770. }
  771. if recordSource["pr_id"] != nil {
  772. record["pr_id"] = recordSource["pr_id"]
  773. }
  774. log.Info("index=" + recordSource["index"].(string))
  775. record["index"] = recordSource["index"]
  776. record["num_comments"] = recordSource["num_comments"]
  777. record["is_closed"] = recordSource["is_closed"]
  778. record["updated_unix"] = recordSource["updated_unix"]
  779. result = append(result, record)
  780. } else {
  781. log.Info("deal issue source error," + err.Error())
  782. }
  783. } else {
  784. log.Info("deal issue source error," + err.Error())
  785. }
  786. }
  787. }
  788. returnObj := &SearchRes{
  789. Total: total,
  790. Result: result,
  791. }
  792. return returnObj
  793. }
  794. func searchPR(ctx *context.Context, TableName string, Key string, Page int, PageSize int, OnlyReturnNum bool) {
  795. /*
  796. 任务,合并请求 ES名称:issue-es-index
  797. 搜索:
  798. name character varying(255) , 标题
  799. content text, 内容
  800. comment text, 评论
  801. 排序:
  802. updated_unix
  803. */
  804. SortBy := ctx.Query("SortBy")
  805. if SortBy == "" {
  806. SortBy = "updated_unix.keyword"
  807. }
  808. ascending := ctx.QueryBool("Ascending")
  809. boolQ := elastic.NewBoolQuery()
  810. isPRQuery := elastic.NewTermQuery("is_pull", "t")
  811. if Key != "" {
  812. boolKeyQ := elastic.NewBoolQuery()
  813. log.Info("issue Key=" + Key)
  814. nameQuery := elastic.NewMatchQuery("name", Key).Boost(2).QueryName("f_first")
  815. contentQuery := elastic.NewMatchQuery("content", Key).Boost(1.5).QueryName("f_second")
  816. commentQuery := elastic.NewMatchQuery("comment", Key).Boost(1).QueryName("f_third")
  817. boolKeyQ.Should(nameQuery, contentQuery, commentQuery)
  818. boolQ.Must(isPRQuery, boolKeyQ)
  819. } else {
  820. boolQ.Must(isPRQuery)
  821. }
  822. res, err := client.Search(TableName).Query(boolQ).Sort(SortBy, ascending).From((Page - 1) * PageSize).Size(PageSize).Highlight(queryHighlight("name", "content", "comment")).Do(ctx.Req.Context())
  823. if err == nil {
  824. searchJson, _ := json.Marshal(res)
  825. log.Info("searchJson=" + string(searchJson))
  826. result := makeIssueResult(res, Key, OnlyReturnNum)
  827. ctx.JSON(200, result)
  828. } else {
  829. log.Info("query es error," + err.Error())
  830. }
  831. }