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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197
  1. package routers
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "sort"
  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. "code.gitea.io/gitea/modules/timeutil"
  13. "github.com/olivere/elastic/v7"
  14. )
  15. type SearchRes struct {
  16. Total int64
  17. Result []map[string]interface{}
  18. PrivateTotal int64
  19. }
  20. var client *elastic.Client
  21. func InitESClient() {
  22. ESSearchUrl := setting.ESSearchURL
  23. var err error
  24. client, err = elastic.NewClient(elastic.SetSniff(false), elastic.SetURL(ESSearchUrl))
  25. if err != nil {
  26. log.Info("es init error.")
  27. //panic(err)
  28. }
  29. }
  30. func EmptySearch(ctx *context.Context) {
  31. log.Info("search template.")
  32. ctx.Data["Keyword"] = ""
  33. ctx.HTML(200, "explore/search_new")
  34. }
  35. func Search(ctx *context.Context) {
  36. log.Info("search template.")
  37. keyword := strings.Trim(ctx.Query("q"), " ")
  38. ctx.Data["Keyword"] = keyword
  39. ctx.Data["SortType"] = "newest"
  40. ctx.HTML(200, "explore/search_new")
  41. }
  42. func SearchApi(ctx *context.Context) {
  43. TableName := ctx.Query("TableName")
  44. Key := ctx.Query("Key")
  45. Page := ctx.QueryInt("Page")
  46. PageSize := ctx.QueryInt("PageSize")
  47. OnlyReturnNum := ctx.QueryBool("OnlyReturnNum")
  48. OnlySearchLabel := ctx.QueryBool("OnlySearchLabel")
  49. if Page <= 0 {
  50. Page = 1
  51. }
  52. if PageSize <= 0 || PageSize > 200 {
  53. PageSize = setting.UI.IssuePagingNum
  54. }
  55. if Key != "" && !OnlyReturnNum {
  56. go models.SaveSearchKeywordToDb(Key)
  57. }
  58. if TableName == "repository" {
  59. if OnlySearchLabel {
  60. searchRepoByLabel(ctx, Key, Page, PageSize)
  61. } else {
  62. searchRepo(ctx, "repository-es-index"+setting.INDEXPOSTFIX, Key, Page, PageSize, OnlyReturnNum)
  63. }
  64. return
  65. } else if TableName == "issue" {
  66. searchIssueOrPr(ctx, "issue-es-index"+setting.INDEXPOSTFIX, Key, Page, PageSize, OnlyReturnNum, "f")
  67. return
  68. } else if TableName == "user" {
  69. searchUserOrOrg(ctx, "user-es-index"+setting.INDEXPOSTFIX, Key, Page, PageSize, true, OnlyReturnNum)
  70. return
  71. } else if TableName == "org" {
  72. searchUserOrOrg(ctx, "user-es-index"+setting.INDEXPOSTFIX, Key, Page, PageSize, false, OnlyReturnNum)
  73. return
  74. } else if TableName == "dataset" {
  75. searchDataSet(ctx, "dataset-es-index"+setting.INDEXPOSTFIX, Key, Page, PageSize, OnlyReturnNum)
  76. return
  77. } else if TableName == "pr" {
  78. searchIssueOrPr(ctx, "issue-es-index"+setting.INDEXPOSTFIX, Key, Page, PageSize, OnlyReturnNum, "t")
  79. //searchPR(ctx, "issue-es-index", Key, Page, PageSize, OnlyReturnNum)
  80. return
  81. }
  82. }
  83. func searchRepoByLabel(ctx *context.Context, Key string, Page int, PageSize int) {
  84. /*
  85. 项目, ES名称: repository-es-index
  86. 搜索:
  87. name character varying(255) , 项目名称
  88. description text, 项目描述
  89. topics json, 标签
  90. 排序:
  91. updated_unix
  92. num_watches,
  93. num_stars,
  94. num_forks,
  95. */
  96. SortBy := ctx.Query("SortBy")
  97. PrivateTotal := ctx.QueryInt("PrivateTotal")
  98. WebTotal := ctx.QueryInt("WebTotal")
  99. ascending := ctx.QueryBool("Ascending")
  100. language := ctx.Query("language")
  101. if language == "" {
  102. language = "zh-CN"
  103. }
  104. from := (Page - 1) * PageSize
  105. resultObj := &SearchRes{}
  106. log.Info("WebTotal=" + fmt.Sprint(WebTotal))
  107. log.Info("PrivateTotal=" + fmt.Sprint(PrivateTotal))
  108. resultObj.Result = make([]map[string]interface{}, 0)
  109. if from == 0 {
  110. WebTotal = 0
  111. }
  112. if ctx.User != nil && (from < PrivateTotal || from == 0) {
  113. orderBy := models.SearchOrderByRecentUpdated
  114. switch SortBy {
  115. case "updated_unix.keyword":
  116. orderBy = models.SearchOrderByRecentUpdated
  117. case "num_stars":
  118. orderBy = models.SearchOrderByStarsReverse
  119. case "num_forks":
  120. orderBy = models.SearchOrderByForksReverse
  121. case "num_watches":
  122. orderBy = models.SearchOrderByWatches
  123. }
  124. log.Info("actor is null?:" + fmt.Sprint(ctx.User == nil))
  125. repos, count, err := models.SearchRepository(&models.SearchRepoOptions{
  126. ListOptions: models.ListOptions{
  127. Page: Page,
  128. PageSize: PageSize,
  129. },
  130. Actor: ctx.User,
  131. OrderBy: orderBy,
  132. Private: true,
  133. OnlyPrivate: true,
  134. TopicOnly: true,
  135. TopicName: Key,
  136. IncludeDescription: setting.UI.SearchRepoDescription,
  137. })
  138. if err != nil {
  139. ctx.JSON(200, "")
  140. return
  141. }
  142. resultObj.PrivateTotal = count
  143. if repos.Len() > 0 {
  144. log.Info("Query private repo number is:" + fmt.Sprint(repos.Len()))
  145. makePrivateRepo(repos, resultObj, Key, language)
  146. } else {
  147. log.Info("not found private repo,keyword=" + Key)
  148. }
  149. if repos.Len() >= PageSize {
  150. if WebTotal > 0 {
  151. resultObj.Total = int64(WebTotal)
  152. ctx.JSON(200, resultObj)
  153. return
  154. }
  155. }
  156. } else {
  157. if ctx.User == nil {
  158. resultObj.PrivateTotal = 0
  159. } else {
  160. resultObj.PrivateTotal = int64(PrivateTotal)
  161. }
  162. }
  163. from = from - PrivateTotal
  164. if from < 0 {
  165. from = 0
  166. }
  167. Size := PageSize - len(resultObj.Result)
  168. log.Info("query searchRepoByLabel start")
  169. if Key != "" {
  170. boolQ := elastic.NewBoolQuery()
  171. topicsQuery := elastic.NewMatchQuery("topics", Key)
  172. boolQ.Should(topicsQuery)
  173. res, err := client.Search("repository-es-index").Query(boolQ).SortBy(getSort(SortBy, ascending, "updated_unix.keyword", false)...).From(from).Size(Size).Highlight(queryHighlight("topics")).Do(ctx.Req.Context())
  174. if err == nil {
  175. searchJson, _ := json.Marshal(res)
  176. log.Info("searchJson=" + string(searchJson))
  177. esresult := makeRepoResult(res, "", false, language)
  178. resultObj.Total = resultObj.PrivateTotal + esresult.Total
  179. resultObj.Result = append(resultObj.Result, esresult.Result...)
  180. ctx.JSON(200, resultObj)
  181. } else {
  182. log.Info("query es error," + err.Error())
  183. ctx.JSON(200, "")
  184. }
  185. } else {
  186. ctx.JSON(200, "")
  187. }
  188. }
  189. func getSort(SortBy string, ascending bool, secondSortBy string, secondAscending bool) []elastic.Sorter {
  190. sort := make([]elastic.Sorter, 0)
  191. if SortBy == "default" || SortBy == "" {
  192. sort = append(sort, elastic.NewScoreSort())
  193. if secondSortBy != "" {
  194. log.Info("SortBy=" + SortBy + " secondSortBy=" + secondSortBy)
  195. sort = append(sort, elastic.NewFieldSort(secondSortBy).Order(secondAscending))
  196. }
  197. } else {
  198. sort = append(sort, elastic.NewFieldSort(SortBy).Order(ascending))
  199. }
  200. log.Info("sort size=" + fmt.Sprint(len(sort)))
  201. return sort
  202. }
  203. func searchRepo(ctx *context.Context, TableName string, Key string, Page int, PageSize int, OnlyReturnNum bool) {
  204. /*
  205. 项目, ES名称: repository-es-index
  206. 搜索:
  207. name character varying(255) , 项目名称
  208. description text, 项目描述
  209. topics json, 标签
  210. 排序:
  211. updated_unix
  212. num_watches,
  213. num_stars,
  214. num_forks,
  215. */
  216. SortBy := ctx.Query("SortBy")
  217. PrivateTotal := ctx.QueryInt("PrivateTotal")
  218. WebTotal := ctx.QueryInt("WebTotal")
  219. ascending := ctx.QueryBool("Ascending")
  220. from := (Page - 1) * PageSize
  221. resultObj := &SearchRes{}
  222. log.Info("WebTotal=" + fmt.Sprint(WebTotal))
  223. log.Info("PrivateTotal=" + fmt.Sprint(PrivateTotal))
  224. resultObj.Result = make([]map[string]interface{}, 0)
  225. if from == 0 {
  226. WebTotal = 0
  227. }
  228. language := ctx.Query("language")
  229. if language == "" {
  230. language = "zh-CN"
  231. }
  232. if ctx.User != nil && (from < PrivateTotal || from == 0) {
  233. orderBy := models.SearchOrderByRecentUpdated
  234. switch SortBy {
  235. case "updated_unix.keyword":
  236. orderBy = models.SearchOrderByRecentUpdated
  237. case "num_stars":
  238. orderBy = models.SearchOrderByStarsReverse
  239. case "num_forks":
  240. orderBy = models.SearchOrderByForksReverse
  241. case "num_watches":
  242. orderBy = models.SearchOrderByWatches
  243. }
  244. log.Info("actor is null?:" + fmt.Sprint(ctx.User == nil))
  245. repos, count, err := models.SearchRepository(&models.SearchRepoOptions{
  246. ListOptions: models.ListOptions{
  247. Page: Page,
  248. PageSize: PageSize,
  249. },
  250. Actor: ctx.User,
  251. OrderBy: orderBy,
  252. Private: true,
  253. OnlyPrivate: true,
  254. Keyword: Key,
  255. IncludeDescription: setting.UI.SearchRepoDescription,
  256. OnlySearchPrivate: true,
  257. })
  258. if err != nil {
  259. ctx.JSON(200, "")
  260. return
  261. }
  262. resultObj.PrivateTotal = count
  263. if repos.Len() > 0 {
  264. log.Info("Query private repo number is:" + fmt.Sprint(repos.Len()))
  265. makePrivateRepo(repos, resultObj, Key, language)
  266. } else {
  267. log.Info("not found private repo,keyword=" + Key)
  268. }
  269. if repos.Len() >= PageSize {
  270. if WebTotal > 0 {
  271. resultObj.Total = int64(WebTotal)
  272. ctx.JSON(200, resultObj)
  273. return
  274. }
  275. }
  276. } else {
  277. if ctx.User == nil {
  278. resultObj.PrivateTotal = 0
  279. } else {
  280. resultObj.PrivateTotal = int64(PrivateTotal)
  281. }
  282. }
  283. from = from - PrivateTotal
  284. if from < 0 {
  285. from = 0
  286. }
  287. Size := PageSize - len(resultObj.Result)
  288. log.Info("query searchRepo start")
  289. if Key != "" {
  290. boolQ := elastic.NewBoolQuery()
  291. nameQuery := elastic.NewMatchQuery("alias", Key).Boost(1024).QueryName("f_first")
  292. descriptionQuery := elastic.NewMatchQuery("description", Key).Boost(1.5).QueryName("f_second")
  293. topicsQuery := elastic.NewMatchQuery("topics", Key).Boost(1).QueryName("f_third")
  294. boolQ.Should(nameQuery, descriptionQuery, topicsQuery)
  295. res, err := client.Search(TableName).Query(boolQ).SortBy(getSort(SortBy, ascending, "num_stars", false)...).From(from).Size(Size).Highlight(queryHighlight("alias", "description", "topics")).Do(ctx.Req.Context())
  296. if err == nil {
  297. searchJson, _ := json.Marshal(res)
  298. log.Info("searchJson=" + string(searchJson))
  299. esresult := makeRepoResult(res, Key, OnlyReturnNum, language)
  300. resultObj.Total = resultObj.PrivateTotal + esresult.Total
  301. isNeedSort := false
  302. if len(resultObj.Result) > 0 {
  303. isNeedSort = true
  304. }
  305. resultObj.Result = append(resultObj.Result, esresult.Result...)
  306. if isNeedSort {
  307. sortRepo(resultObj.Result, SortBy, ascending)
  308. }
  309. ctx.JSON(200, resultObj)
  310. } else {
  311. log.Info("query es error," + err.Error())
  312. ctx.JSON(200, "")
  313. }
  314. } else {
  315. log.Info("query all content.")
  316. //搜索的属性要指定{"timestamp":{"unmapped_type":"date"}}
  317. res, err := client.Search(TableName).SortBy(getSort(SortBy, ascending, "updated_unix.keyword", false)...).From(from).Size(Size).Do(ctx.Req.Context())
  318. if err == nil {
  319. searchJson, _ := json.Marshal(res)
  320. log.Info("searchJson=" + string(searchJson))
  321. esresult := makeRepoResult(res, "", OnlyReturnNum, language)
  322. resultObj.Total = resultObj.PrivateTotal + esresult.Total
  323. resultObj.Result = append(resultObj.Result, esresult.Result...)
  324. ctx.JSON(200, resultObj)
  325. } else {
  326. log.Info("query es error," + err.Error())
  327. ctx.JSON(200, "")
  328. }
  329. }
  330. }
  331. func sortRepo(Result []map[string]interface{}, SortBy string, ascending bool) {
  332. orderBy := ""
  333. switch SortBy {
  334. case "updated_unix.keyword":
  335. orderBy = "updated_unix"
  336. case "num_stars":
  337. orderBy = "num_stars"
  338. case "num_forks":
  339. orderBy = "num_forks"
  340. case "num_watches":
  341. orderBy = "num_watches"
  342. }
  343. sort.Slice(Result, func(i, j int) bool {
  344. return getInt(Result[i][orderBy], orderBy) > getInt(Result[j][orderBy], orderBy)
  345. })
  346. }
  347. func getInt(tmp interface{}, orderBy string) int64 {
  348. timeInt, err := strconv.ParseInt(fmt.Sprint(tmp), 10, 64)
  349. if err == nil {
  350. return timeInt
  351. } else {
  352. log.Info("convert " + orderBy + " error type=" + fmt.Sprint(tmp))
  353. }
  354. return -1
  355. }
  356. func makePrivateRepo(repos models.RepositoryList, res *SearchRes, keyword string, language string) {
  357. for _, repo := range repos {
  358. record := make(map[string]interface{})
  359. record["id"] = repo.ID
  360. record["name"] = makeHighLight(keyword, repo.Name)
  361. record["real_name"] = repo.Name
  362. record["owner_name"] = repo.OwnerName
  363. record["description"] = truncLongText(makeHighLight(keyword, repo.Description), true)
  364. hightTopics := make([]string, 0)
  365. if len(repo.Topics) > 0 {
  366. for _, t := range repo.Topics {
  367. hightTopics = append(hightTopics, makeHighLight(keyword, t))
  368. }
  369. }
  370. record["hightTopics"] = hightTopics
  371. record["num_watches"] = repo.NumWatches
  372. record["num_stars"] = repo.NumStars
  373. record["num_forks"] = repo.NumForks
  374. record["alias"] = truncLongText(makeHighLight(keyword, repo.Alias), true)
  375. record["lower_alias"] = repo.LowerAlias
  376. record["topics"] = repo.Topics
  377. record["avatar"] = repo.RelAvatarLink()
  378. if len(repo.RelAvatarLink()) == 0 {
  379. record["avatar"] = setting.RepositoryAvatarFallbackImage
  380. }
  381. record["updated_unix"] = repo.UpdatedUnix
  382. record["updated_html"] = timeutil.TimeSinceUnix(repo.UpdatedUnix, language)
  383. lang, err := repo.GetTopLanguageStats(1)
  384. if err == nil && len(lang) > 0 {
  385. record["lang"] = lang[0].Language
  386. } else {
  387. record["lang"] = ""
  388. }
  389. record["is_private"] = true
  390. res.Result = append(res.Result, record)
  391. }
  392. }
  393. func makeHighLight(keyword string, dest string) string {
  394. dest = replaceIngoreUpperOrLower(dest, strings.ToLower(dest), strings.ToLower(keyword))
  395. return dest
  396. }
  397. func replaceIngoreUpperOrLower(dest string, destLower string, keywordLower string) string {
  398. re := ""
  399. last := 0
  400. lenDestLower := len(destLower)
  401. lenkeywordLower := len(keywordLower)
  402. for i := 0; i < lenDestLower; i++ {
  403. if destLower[i] == keywordLower[0] {
  404. isFind := true
  405. for j := 1; j < lenkeywordLower; j++ {
  406. if (i+j) < lenDestLower && keywordLower[j] != destLower[i+j] {
  407. isFind = false
  408. break
  409. }
  410. }
  411. if isFind && (i+lenkeywordLower) <= lenDestLower {
  412. re += dest[last:i] + "\u003cfont color='red'\u003e" + dest[i:(i+lenkeywordLower)] + "\u003c/font\u003e"
  413. i = i + lenkeywordLower
  414. last = i
  415. }
  416. }
  417. }
  418. if last < lenDestLower {
  419. re += dest[last:lenDestLower]
  420. }
  421. return re
  422. }
  423. func makeRepoResult(sRes *elastic.SearchResult, Key string, OnlyReturnNum bool, language string) *SearchRes {
  424. total := sRes.Hits.TotalHits.Value
  425. result := make([]map[string]interface{}, 0)
  426. if !OnlyReturnNum {
  427. for i, hit := range sRes.Hits.Hits {
  428. log.Info("this is repo query " + fmt.Sprint(i) + " result.")
  429. recordSource := make(map[string]interface{})
  430. source, err := hit.Source.MarshalJSON()
  431. if err == nil {
  432. err = json.Unmarshal(source, &recordSource)
  433. if err == nil {
  434. record := make(map[string]interface{})
  435. record["id"] = hit.Id
  436. record["alias"] = getLabelValue("alias", recordSource, hit.Highlight)
  437. record["real_name"] = recordSource["name"]
  438. record["owner_name"] = recordSource["owner_name"]
  439. if recordSource["description"] != nil {
  440. desc := getLabelValue("description", recordSource, hit.Highlight)
  441. record["description"] = dealLongText(desc, Key, hit.MatchedQueries)
  442. } else {
  443. record["description"] = ""
  444. }
  445. record["hightTopics"] = jsonStrToArray(getLabelValue("topics", recordSource, hit.Highlight))
  446. record["num_watches"] = recordSource["num_watches"]
  447. record["num_stars"] = recordSource["num_stars"]
  448. record["num_forks"] = recordSource["num_forks"]
  449. record["lower_alias"] = recordSource["lower_alias"]
  450. if recordSource["topics"] != nil {
  451. topicsStr := recordSource["topics"].(string)
  452. log.Info("topicsStr=" + topicsStr)
  453. if topicsStr != "null" {
  454. record["topics"] = jsonStrToArray(topicsStr)
  455. }
  456. }
  457. if recordSource["avatar"] != nil {
  458. avatarstr := recordSource["avatar"].(string)
  459. if len(avatarstr) == 0 {
  460. record["avatar"] = setting.RepositoryAvatarFallbackImage
  461. } else {
  462. record["avatar"] = setting.AppSubURL + "/repo-avatars/" + avatarstr
  463. }
  464. }
  465. record["updated_unix"] = recordSource["updated_unix"]
  466. setUpdateHtml(record, recordSource["updated_unix"].(string), language)
  467. record["lang"] = recordSource["lang"]
  468. record["is_private"] = false
  469. result = append(result, record)
  470. } else {
  471. log.Info("deal repo source error," + err.Error())
  472. }
  473. } else {
  474. log.Info("deal repo source error," + err.Error())
  475. }
  476. }
  477. }
  478. returnObj := &SearchRes{
  479. Total: total,
  480. Result: result,
  481. }
  482. return returnObj
  483. }
  484. func setUpdateHtml(record map[string]interface{}, updated_unix string, language string) {
  485. timeInt, err := strconv.ParseInt(updated_unix, 10, 64)
  486. if err == nil {
  487. record["updated_html"] = timeutil.TimeSinceUnix(timeutil.TimeStamp(timeInt), language)
  488. }
  489. }
  490. func jsonStrToArray(str string) []string {
  491. b := []byte(str)
  492. strs := make([]string, 0)
  493. err := json.Unmarshal(b, &strs)
  494. if err != nil {
  495. log.Info("convert str arrar error, str=" + str)
  496. }
  497. return strs
  498. }
  499. func dealLongText(text string, Key string, MatchedQueries []string) string {
  500. var isNeedToDealText bool
  501. isNeedToDealText = false
  502. if len(MatchedQueries) > 0 && Key != "" {
  503. if MatchedQueries[0] == "f_second" || MatchedQueries[0] == "f_third" {
  504. isNeedToDealText = true
  505. }
  506. }
  507. return truncLongText(text, isNeedToDealText)
  508. }
  509. func truncLongText(text string, isNeedToDealText bool) string {
  510. startStr := "color="
  511. textRune := []rune(text)
  512. stringlen := len(textRune)
  513. if isNeedToDealText && stringlen > 200 {
  514. index := findFont(textRune, []rune(startStr))
  515. if index > 0 {
  516. start := index - 50
  517. if start < 0 {
  518. start = 0
  519. }
  520. end := index + 150
  521. if end >= stringlen {
  522. end = stringlen
  523. }
  524. return trimFontHtml(textRune[start:end]) + "..."
  525. } else {
  526. return trimFontHtml(textRune[0:200]) + "..."
  527. }
  528. } else {
  529. if stringlen > 200 {
  530. return trimFontHtml(textRune[0:200]) + "..."
  531. } else {
  532. return text
  533. }
  534. }
  535. }
  536. func trimFontHtml(text []rune) string {
  537. startRune := rune('<')
  538. endRune := rune('>')
  539. count := 0
  540. i := 0
  541. for ; i < len(text); i++ {
  542. if text[i] == startRune { //start <
  543. re := false
  544. j := i + 1
  545. for ; j < len(text); j++ {
  546. if text[j] == endRune {
  547. re = true
  548. break
  549. }
  550. }
  551. if re { //found >
  552. i = j
  553. count++
  554. } else {
  555. if count%2 == 1 {
  556. return string(text[0:i]) + "</font>"
  557. } else {
  558. return string(text[0:i])
  559. }
  560. }
  561. }
  562. }
  563. if count%2 == 1 {
  564. return string(text[0:i]) + "</font>"
  565. } else {
  566. return string(text[0:i])
  567. }
  568. }
  569. func trimHrefHtml(result string) string {
  570. result = strings.Replace(result, "</a>", "", -1)
  571. result = strings.Replace(result, "\n", "", -1)
  572. var index int
  573. for {
  574. index = findSubstr(result, 0, "<a")
  575. if index != -1 {
  576. sIndex := findSubstr(result, index+2, ">")
  577. if sIndex != -1 {
  578. result = result[0:index] + result[sIndex+1:]
  579. } else {
  580. result = result[0:index] + result[index+2:]
  581. }
  582. } else {
  583. break
  584. }
  585. }
  586. return result
  587. }
  588. func findFont(text []rune, childText []rune) int {
  589. for i := 0; i < len(text); i++ {
  590. if text[i] == childText[0] {
  591. re := true
  592. for j, k := range childText {
  593. if k != text[i+j] {
  594. re = false
  595. break
  596. }
  597. }
  598. if re {
  599. return i
  600. }
  601. }
  602. }
  603. return -1
  604. }
  605. func findSubstr(text string, startindex int, childText string) int {
  606. for i := startindex; i < len(text); i++ {
  607. if text[i] == childText[0] {
  608. re := true
  609. for k := range childText {
  610. if childText[k] != text[i+k] {
  611. re = false
  612. break
  613. }
  614. }
  615. if re {
  616. return i
  617. }
  618. }
  619. }
  620. return -1
  621. }
  622. func searchUserOrOrg(ctx *context.Context, TableName string, Key string, Page int, PageSize int, IsQueryUser bool, OnlyReturnNum bool) {
  623. /*
  624. 用户或者组织 ES名称: user-es-index
  625. 搜索:
  626. name , 名称
  627. full_name 全名
  628. description 描述或者简介
  629. 排序:
  630. created_unix
  631. 名称字母序
  632. */
  633. SortBy := ctx.Query("SortBy")
  634. ascending := ctx.QueryBool("Ascending")
  635. boolQ := elastic.NewBoolQuery()
  636. typeValue := 1
  637. if IsQueryUser {
  638. typeValue = 0
  639. }
  640. UserOrOrgQuery := elastic.NewTermQuery("type", typeValue)
  641. if Key != "" {
  642. boolKeyQ := elastic.NewBoolQuery()
  643. log.Info("user or org Key=" + Key)
  644. nameQuery := elastic.NewMatchQuery("name", Key).Boost(2).QueryName("f_first")
  645. full_nameQuery := elastic.NewMatchQuery("full_name", Key).Boost(1.5).QueryName("f_second")
  646. descriptionQuery := elastic.NewMatchQuery("description", Key).Boost(1).QueryName("f_third")
  647. boolKeyQ.Should(nameQuery, full_nameQuery, descriptionQuery)
  648. boolQ.Must(UserOrOrgQuery, boolKeyQ)
  649. } else {
  650. boolQ.Must(UserOrOrgQuery)
  651. }
  652. res, err := client.Search(TableName).Query(boolQ).SortBy(getSort(SortBy, ascending, "updated_unix.keyword", false)...).From((Page - 1) * PageSize).Size(PageSize).Highlight(queryHighlight("name", "full_name", "description")).Do(ctx.Req.Context())
  653. if err == nil {
  654. searchJson, _ := json.Marshal(res)
  655. log.Info("searchJson=" + string(searchJson))
  656. result := makeUserOrOrgResult(res, Key, ctx, OnlyReturnNum)
  657. ctx.JSON(200, result)
  658. } else {
  659. log.Info("query es error," + err.Error())
  660. ctx.JSON(200, "")
  661. }
  662. }
  663. func getLabelValue(key string, recordSource map[string]interface{}, searchHighliht elastic.SearchHitHighlight) string {
  664. if value, ok := searchHighliht[key]; !ok {
  665. if recordSource[key] != nil {
  666. return recordSource[key].(string)
  667. } else {
  668. return ""
  669. }
  670. } else {
  671. return value[0]
  672. }
  673. }
  674. func makeUserOrOrgResult(sRes *elastic.SearchResult, Key string, ctx *context.Context, OnlyReturnNum bool) *SearchRes {
  675. total := sRes.Hits.TotalHits.Value
  676. result := make([]map[string]interface{}, 0)
  677. if !OnlyReturnNum {
  678. for i, hit := range sRes.Hits.Hits {
  679. log.Info("this is user query " + fmt.Sprint(i) + " result.")
  680. recordSource := make(map[string]interface{})
  681. source, err := hit.Source.MarshalJSON()
  682. if err == nil {
  683. err = json.Unmarshal(source, &recordSource)
  684. if err == nil {
  685. record := make(map[string]interface{})
  686. record["id"] = hit.Id
  687. record["name"] = getLabelValue("name", recordSource, hit.Highlight)
  688. record["real_name"] = recordSource["name"]
  689. record["full_name"] = getLabelValue("full_name", recordSource, hit.Highlight)
  690. if recordSource["description"] != nil {
  691. desc := getLabelValue("description", recordSource, hit.Highlight)
  692. record["description"] = dealLongText(desc, Key, hit.MatchedQueries)
  693. } else {
  694. record["description"] = ""
  695. }
  696. if ctx.User != nil {
  697. record["email"] = recordSource["email"]
  698. } else {
  699. record["email"] = ""
  700. }
  701. record["location"] = recordSource["location"]
  702. record["website"] = recordSource["website"]
  703. record["num_repos"] = recordSource["num_repos"]
  704. record["num_teams"] = recordSource["num_teams"]
  705. record["num_members"] = recordSource["num_members"]
  706. record["avatar"] = strings.TrimRight(setting.AppSubURL, "/") + "/user/avatar/" + recordSource["name"].(string) + "/" + strconv.Itoa(-1)
  707. record["updated_unix"] = recordSource["updated_unix"]
  708. record["created_unix"] = recordSource["created_unix"]
  709. record["add_time"] = getAddTime(recordSource["created_unix"].(string))
  710. result = append(result, record)
  711. } else {
  712. log.Info("deal user source error," + err.Error())
  713. }
  714. } else {
  715. log.Info("deal user source error," + err.Error())
  716. }
  717. }
  718. }
  719. returnObj := &SearchRes{
  720. Total: total,
  721. Result: result,
  722. }
  723. return returnObj
  724. }
  725. func getAddTime(time string) string {
  726. timeInt, err := strconv.ParseInt(time, 10, 64)
  727. if err == nil {
  728. t := timeutil.TimeStamp(timeInt)
  729. return t.FormatShort()
  730. }
  731. return ""
  732. }
  733. func searchDataSet(ctx *context.Context, TableName string, Key string, Page int, PageSize int, OnlyReturnNum bool) {
  734. /*
  735. 数据集,ES名称:dataset-es-index
  736. 搜索:
  737. title , 名称
  738. description 描述
  739. category 标签
  740. file_name 数据集文件名称
  741. 排序:
  742. download_times
  743. */
  744. log.Info("query searchdataset start")
  745. SortBy := ctx.Query("SortBy")
  746. ascending := ctx.QueryBool("Ascending")
  747. PrivateTotal := ctx.QueryInt("PrivateTotal")
  748. WebTotal := ctx.QueryInt("WebTotal")
  749. language := ctx.Query("language")
  750. if language == "" {
  751. language = "zh-CN"
  752. }
  753. from := (Page - 1) * PageSize
  754. if from == 0 {
  755. WebTotal = 0
  756. }
  757. resultObj := &SearchRes{}
  758. log.Info("WebTotal=" + fmt.Sprint(WebTotal))
  759. log.Info("PrivateTotal=" + fmt.Sprint(PrivateTotal))
  760. resultObj.Result = make([]map[string]interface{}, 0)
  761. if ctx.User != nil && (from < PrivateTotal || from == 0) {
  762. log.Info("actor is null?:" + fmt.Sprint(ctx.User == nil))
  763. datasets, count, err := models.SearchDatasetBySQL(Page, PageSize, Key, ctx.User.ID)
  764. if err != nil {
  765. ctx.JSON(200, "")
  766. return
  767. }
  768. resultObj.PrivateTotal = count
  769. datasetSize := len(datasets)
  770. if datasetSize > 0 {
  771. log.Info("Query private dataset number is:" + fmt.Sprint(datasetSize) + " count=" + fmt.Sprint(count))
  772. makePrivateDataSet(datasets, resultObj, Key, language)
  773. } else {
  774. log.Info("not found private dataset, keyword=" + Key)
  775. }
  776. if datasetSize >= PageSize {
  777. if WebTotal > 0 { //next page, not first query.
  778. resultObj.Total = int64(WebTotal)
  779. ctx.JSON(200, resultObj)
  780. return
  781. }
  782. }
  783. } else {
  784. resultObj.PrivateTotal = int64(PrivateTotal)
  785. }
  786. from = from - PrivateTotal
  787. if from < 0 {
  788. from = 0
  789. }
  790. Size := PageSize - len(resultObj.Result)
  791. boolQ := elastic.NewBoolQuery()
  792. if Key != "" {
  793. nameQuery := elastic.NewMatchQuery("title", Key).Boost(2).QueryName("f_first")
  794. descQuery := elastic.NewMatchQuery("description", Key).Boost(1.5).QueryName("f_second")
  795. fileNameQuery := elastic.NewMatchQuery("file_name", Key).Boost(1).QueryName("f_third")
  796. categoryQuery := elastic.NewMatchQuery("category", Key).Boost(1).QueryName("f_fourth")
  797. boolQ.Should(nameQuery, descQuery, categoryQuery, fileNameQuery)
  798. res, err := client.Search(TableName).Query(boolQ).SortBy(getSort(SortBy, ascending, "updated_unix.keyword", false)...).From(from).Size(Size).Highlight(queryHighlight("title", "description", "file_name", "category")).Do(ctx.Req.Context())
  799. if err == nil {
  800. searchJson, _ := json.Marshal(res)
  801. log.Info("searchJson=" + string(searchJson))
  802. esresult := makeDatasetResult(res, Key, OnlyReturnNum, language)
  803. resultObj.Total = resultObj.PrivateTotal + esresult.Total
  804. log.Info("query dataset es count=" + fmt.Sprint(esresult.Total) + " total=" + fmt.Sprint(resultObj.Total))
  805. resultObj.Result = append(resultObj.Result, esresult.Result...)
  806. ctx.JSON(200, resultObj)
  807. } else {
  808. log.Info("query es error," + err.Error())
  809. }
  810. } else {
  811. log.Info("query all datasets.")
  812. //搜索的属性要指定{"timestamp":{"unmapped_type":"date"}}
  813. res, err := client.Search(TableName).SortBy(getSort(SortBy, ascending, "updated_unix.keyword", false)...).From(from).Size(Size).Do(ctx.Req.Context())
  814. if err == nil {
  815. searchJson, _ := json.Marshal(res)
  816. log.Info("searchJson=" + string(searchJson))
  817. esresult := makeDatasetResult(res, "", OnlyReturnNum, language)
  818. resultObj.Total = resultObj.PrivateTotal + esresult.Total
  819. log.Info("query dataset es count=" + fmt.Sprint(esresult.Total) + " total=" + fmt.Sprint(resultObj.Total))
  820. resultObj.Result = append(resultObj.Result, esresult.Result...)
  821. ctx.JSON(200, resultObj)
  822. } else {
  823. log.Info("query es error," + err.Error())
  824. ctx.JSON(200, "")
  825. }
  826. }
  827. }
  828. func makePrivateDataSet(datasets []*models.Dataset, res *SearchRes, Key string, language string) {
  829. for _, dataset := range datasets {
  830. record := make(map[string]interface{})
  831. record["id"] = dataset.ID
  832. userId := dataset.UserID
  833. user, errUser := models.GetUserByID(userId)
  834. if errUser == nil {
  835. record["owerName"] = user.GetDisplayName()
  836. record["avatar"] = user.RelAvatarLink()
  837. }
  838. repo, errRepo := models.GetRepositoryByID(dataset.RepoID)
  839. if errRepo == nil {
  840. log.Info("repo_url=" + repo.FullName())
  841. record["repoUrl"] = repo.FullName()
  842. record["avatar"] = repo.RelAvatarLink()
  843. } else {
  844. log.Info("repo err=" + errRepo.Error())
  845. }
  846. record["title"] = makeHighLight(Key, dataset.Title)
  847. record["description"] = truncLongText(makeHighLight(Key, dataset.Description), true)
  848. record["category"] = dataset.Category
  849. record["task"] = dataset.Task
  850. record["download_times"] = dataset.DownloadTimes
  851. record["created_unix"] = dataset.CreatedUnix
  852. record["updated_unix"] = repo.UpdatedUnix
  853. record["updated_html"] = timeutil.TimeSinceUnix(repo.UpdatedUnix, language)
  854. res.Result = append(res.Result, record)
  855. }
  856. }
  857. func makeDatasetResult(sRes *elastic.SearchResult, Key string, OnlyReturnNum bool, language string) *SearchRes {
  858. total := sRes.Hits.TotalHits.Value
  859. result := make([]map[string]interface{}, 0)
  860. if !OnlyReturnNum {
  861. for i, hit := range sRes.Hits.Hits {
  862. log.Info("this is dataset query " + fmt.Sprint(i) + " result.")
  863. recordSource := make(map[string]interface{})
  864. source, err := hit.Source.MarshalJSON()
  865. if err == nil {
  866. err = json.Unmarshal(source, &recordSource)
  867. if err == nil {
  868. record := make(map[string]interface{})
  869. record["id"] = hit.Id
  870. userIdStr := recordSource["user_id"].(string)
  871. userId, cerr := strconv.ParseInt(userIdStr, 10, 64)
  872. if cerr == nil {
  873. user, errUser := models.GetUserByID(userId)
  874. if errUser == nil {
  875. record["owerName"] = user.GetDisplayName()
  876. record["avatar"] = user.RelAvatarLink()
  877. }
  878. }
  879. setRepoInfo(recordSource, record)
  880. record["title"] = getLabelValue("title", recordSource, hit.Highlight)
  881. record["category"] = getLabelValue("category", recordSource, hit.Highlight)
  882. if recordSource["description"] != nil {
  883. desc := getLabelValue("description", recordSource, hit.Highlight)
  884. record["description"] = dealLongText(desc, Key, hit.MatchedQueries)
  885. } else {
  886. record["description"] = ""
  887. }
  888. record["file_name"] = getDatasetFileName(getLabelValue("file_name", recordSource, hit.Highlight))
  889. record["task"] = recordSource["task"]
  890. record["download_times"] = recordSource["download_times"]
  891. record["created_unix"] = recordSource["created_unix"]
  892. setUpdateHtml(record, recordSource["updated_unix"].(string), language)
  893. result = append(result, record)
  894. } else {
  895. log.Info("deal dataset source error," + err.Error())
  896. }
  897. } else {
  898. log.Info("deal dataset source error," + err.Error())
  899. }
  900. }
  901. }
  902. returnObj := &SearchRes{
  903. Total: total,
  904. Result: result,
  905. }
  906. return returnObj
  907. }
  908. func getDatasetFileName(fileName string) string {
  909. slices := strings.Split(fileName, "-#,#-")
  910. fileName = strings.Join(slices, ", ")
  911. return fileName
  912. }
  913. func searchIssueOrPr(ctx *context.Context, TableName string, Key string, Page int, PageSize int, OnlyReturnNum bool, issueOrPr string) {
  914. /*
  915. 任务,合并请求 ES名称:issue-es-index
  916. 搜索:
  917. name character varying(255) , 标题
  918. content text, 内容
  919. comment text, 评论
  920. 排序:
  921. updated_unix
  922. */
  923. SortBy := ctx.Query("SortBy")
  924. ascending := ctx.QueryBool("Ascending")
  925. PrivateTotal := ctx.QueryInt("PrivateTotal")
  926. WebTotal := ctx.QueryInt("WebTotal")
  927. language := ctx.Query("language")
  928. if language == "" {
  929. language = "zh-CN"
  930. }
  931. from := (Page - 1) * PageSize
  932. if from == 0 {
  933. WebTotal = 0
  934. }
  935. resultObj := &SearchRes{}
  936. log.Info("WebTotal=" + fmt.Sprint(WebTotal))
  937. log.Info("PrivateTotal=" + fmt.Sprint(PrivateTotal))
  938. resultObj.Result = make([]map[string]interface{}, 0)
  939. isPull := false
  940. if issueOrPr == "t" {
  941. isPull = true
  942. }
  943. if ctx.User != nil && (from < PrivateTotal || from == 0) {
  944. log.Info("actor is null?:" + fmt.Sprint(ctx.User == nil))
  945. issues, count, err := models.SearchPrivateIssueOrPr(Page, PageSize, Key, isPull, ctx.User.ID)
  946. if err != nil {
  947. ctx.JSON(200, "")
  948. return
  949. }
  950. resultObj.PrivateTotal = count
  951. issuesSize := len(issues)
  952. if issuesSize > 0 {
  953. log.Info("Query private repo issue number is:" + fmt.Sprint(issuesSize) + " count=" + fmt.Sprint(count))
  954. makePrivateIssueOrPr(issues, resultObj, Key, language)
  955. } else {
  956. log.Info("not found private repo issue,keyword=" + Key)
  957. }
  958. if issuesSize >= PageSize {
  959. if WebTotal > 0 { //next page, not first query.
  960. resultObj.Total = int64(WebTotal)
  961. ctx.JSON(200, resultObj)
  962. return
  963. }
  964. }
  965. } else {
  966. resultObj.PrivateTotal = int64(PrivateTotal)
  967. }
  968. from = from - PrivateTotal
  969. if from < 0 {
  970. from = 0
  971. }
  972. Size := PageSize - len(resultObj.Result)
  973. boolQ := elastic.NewBoolQuery()
  974. isIssueQuery := elastic.NewTermQuery("is_pull", issueOrPr)
  975. if Key != "" {
  976. boolKeyQ := elastic.NewBoolQuery()
  977. log.Info("issue Key=" + Key)
  978. nameQuery := elastic.NewMatchQuery("name", Key).Boost(2).QueryName("f_first")
  979. contentQuery := elastic.NewMatchQuery("content", Key).Boost(1.5).QueryName("f_second")
  980. commentQuery := elastic.NewMatchQuery("comment", Key).Boost(1).QueryName("f_third")
  981. boolKeyQ.Should(nameQuery, contentQuery, commentQuery)
  982. boolQ.Must(isIssueQuery, boolKeyQ)
  983. } else {
  984. boolQ.Must(isIssueQuery)
  985. }
  986. res, err := client.Search(TableName).Query(boolQ).SortBy(getSort(SortBy, ascending, "updated_unix.keyword", false)...).From(from).Size(Size).Highlight(queryHighlight("name", "content", "comment")).Do(ctx.Req.Context())
  987. if err == nil {
  988. searchJson, _ := json.Marshal(res)
  989. log.Info("searchJson=" + string(searchJson))
  990. esresult := makeIssueResult(res, Key, OnlyReturnNum, language)
  991. resultObj.Total = resultObj.PrivateTotal + esresult.Total
  992. log.Info("query issue es count=" + fmt.Sprint(esresult.Total) + " total=" + fmt.Sprint(resultObj.Total))
  993. resultObj.Result = append(resultObj.Result, esresult.Result...)
  994. ctx.JSON(200, resultObj)
  995. } else {
  996. log.Info("query es error," + err.Error())
  997. }
  998. }
  999. func queryHighlight(names ...string) *elastic.Highlight {
  1000. re := elastic.NewHighlight()
  1001. for i := 0; i < len(names); i++ {
  1002. field := &elastic.HighlighterField{
  1003. Name: names[i],
  1004. }
  1005. re.Fields(field)
  1006. }
  1007. re.PreTags("<font color='red'>")
  1008. re.PostTags("</font>")
  1009. return re
  1010. }
  1011. func setRepoInfo(recordSource map[string]interface{}, record map[string]interface{}) {
  1012. repoIdstr := recordSource["repo_id"].(string)
  1013. repoId, cerr := strconv.ParseInt(repoIdstr, 10, 64)
  1014. if cerr == nil {
  1015. repo, errRepo := models.GetRepositoryByID(repoId)
  1016. if errRepo == nil {
  1017. log.Info("repo_url=" + repo.FullName())
  1018. record["repoUrl"] = repo.FullName()
  1019. record["avatar"] = repo.RelAvatarLink()
  1020. } else {
  1021. log.Info("repo err=" + errRepo.Error())
  1022. }
  1023. } else {
  1024. log.Info("parse int err=" + cerr.Error())
  1025. }
  1026. }
  1027. func makePrivateIssueOrPr(issues []*models.Issue, res *SearchRes, Key string, language string) {
  1028. for _, issue := range issues {
  1029. record := make(map[string]interface{})
  1030. record["id"] = issue.ID
  1031. record["repo_id"] = issue.RepoID
  1032. repo, errRepo := models.GetRepositoryByID(issue.RepoID)
  1033. if errRepo == nil {
  1034. log.Info("repo_url=" + repo.FullName())
  1035. record["repoUrl"] = repo.FullName()
  1036. record["avatar"] = repo.RelAvatarLink()
  1037. } else {
  1038. log.Info("repo err=" + errRepo.Error())
  1039. }
  1040. record["name"] = makeHighLight(Key, issue.Title)
  1041. record["content"] = truncLongText(makeHighLight(Key, issue.Content), true)
  1042. if issue.IsPull {
  1043. pr, err1 := issue.GetPullRequest()
  1044. if err1 == nil && pr != nil {
  1045. record["pr_id"] = pr.ID
  1046. }
  1047. }
  1048. record["index"] = issue.Index
  1049. record["num_comments"] = issue.NumComments
  1050. record["is_closed"] = issue.IsClosed
  1051. record["updated_unix"] = issue.UpdatedUnix
  1052. record["updated_html"] = timeutil.TimeSinceUnix(issue.UpdatedUnix, language)
  1053. res.Result = append(res.Result, record)
  1054. }
  1055. }
  1056. func makeIssueResult(sRes *elastic.SearchResult, Key string, OnlyReturnNum bool, language string) *SearchRes {
  1057. total := sRes.Hits.TotalHits.Value
  1058. result := make([]map[string]interface{}, 0)
  1059. if !OnlyReturnNum {
  1060. for i, hit := range sRes.Hits.Hits {
  1061. log.Info("this is issue query " + fmt.Sprint(i) + " result.")
  1062. recordSource := make(map[string]interface{})
  1063. source, err := hit.Source.MarshalJSON()
  1064. if err == nil {
  1065. err = json.Unmarshal(source, &recordSource)
  1066. if err == nil {
  1067. record := make(map[string]interface{})
  1068. record["id"] = hit.Id
  1069. record["repo_id"] = recordSource["repo_id"]
  1070. log.Info("recordSource[\"repo_id\"]=" + fmt.Sprint(recordSource["repo_id"]))
  1071. setRepoInfo(recordSource, record)
  1072. record["name"] = getLabelValue("name", recordSource, hit.Highlight)
  1073. if recordSource["content"] != nil {
  1074. desc := getLabelValue("content", recordSource, hit.Highlight)
  1075. record["content"] = dealLongText(desc, Key, hit.MatchedQueries)
  1076. if _, ok := hit.Highlight["content"]; !ok {
  1077. if _, ok_comment := hit.Highlight["comment"]; ok_comment {
  1078. desc := getLabelValue("comment", recordSource, hit.Highlight)
  1079. record["content"] = trimHrefHtml(dealLongText(desc, Key, hit.MatchedQueries))
  1080. }
  1081. }
  1082. } else {
  1083. if recordSource["comment"] != nil {
  1084. desc := getLabelValue("comment", recordSource, hit.Highlight)
  1085. record["content"] = dealLongText(desc, Key, hit.MatchedQueries)
  1086. }
  1087. }
  1088. if recordSource["pr_id"] != nil {
  1089. record["pr_id"] = recordSource["pr_id"]
  1090. }
  1091. log.Info("index=" + recordSource["index"].(string))
  1092. record["index"] = recordSource["index"]
  1093. record["num_comments"] = recordSource["num_comments"]
  1094. record["is_closed"] = recordSource["is_closed"]
  1095. record["updated_unix"] = recordSource["updated_unix"]
  1096. setUpdateHtml(record, recordSource["updated_unix"].(string), language)
  1097. result = append(result, record)
  1098. } else {
  1099. log.Info("deal issue source error," + err.Error())
  1100. }
  1101. } else {
  1102. log.Info("deal issue source error," + err.Error())
  1103. }
  1104. }
  1105. }
  1106. returnObj := &SearchRes{
  1107. Total: total,
  1108. Result: result,
  1109. }
  1110. return returnObj
  1111. }