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

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223
  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. esresult := makeRepoResult(res, Key, OnlyReturnNum, language)
  298. setForkRepoOrder(esresult, SortBy)
  299. resultObj.Total = resultObj.PrivateTotal + esresult.Total
  300. isNeedSort := false
  301. if len(resultObj.Result) > 0 {
  302. isNeedSort = true
  303. }
  304. resultObj.Result = append(resultObj.Result, esresult.Result...)
  305. if isNeedSort {
  306. sortRepo(resultObj.Result, SortBy, ascending)
  307. }
  308. ctx.JSON(200, resultObj)
  309. } else {
  310. log.Info("query es error," + err.Error())
  311. ctx.JSON(200, "")
  312. }
  313. } else {
  314. log.Info("query all content.")
  315. //搜索的属性要指定{"timestamp":{"unmapped_type":"date"}}
  316. res, err := client.Search(TableName).SortBy(getSort(SortBy, ascending, "updated_unix.keyword", false)...).From(from).Size(Size).Do(ctx.Req.Context())
  317. if err == nil {
  318. searchJson, _ := json.Marshal(res)
  319. log.Info("searchJson=" + string(searchJson))
  320. esresult := makeRepoResult(res, "", OnlyReturnNum, language)
  321. resultObj.Total = resultObj.PrivateTotal + esresult.Total
  322. resultObj.Result = append(resultObj.Result, esresult.Result...)
  323. ctx.JSON(200, resultObj)
  324. } else {
  325. log.Info("query es error," + err.Error())
  326. ctx.JSON(200, "")
  327. }
  328. }
  329. }
  330. func setForkRepoOrder(esresult *SearchRes, SortBy string) {
  331. if SortBy == "default" || SortBy == "" {
  332. forkidMap := make(map[string]int, 0)
  333. for index, re := range esresult.Result {
  334. if re["fork_id"] != nil {
  335. fork_id := re["fork_id"].(string)
  336. if _, ok := forkidMap[fork_id]; !ok {
  337. forkidMap[fork_id] = index
  338. }
  339. }
  340. }
  341. for key, value := range forkidMap {
  342. for index, re := range esresult.Result {
  343. if re["id"].(string) == key {
  344. if value < index { //swap
  345. tmp := esresult.Result[index]
  346. esresult.Result[index] = esresult.Result[value]
  347. esresult.Result[value] = tmp
  348. break
  349. }
  350. }
  351. }
  352. }
  353. }
  354. }
  355. func sortRepo(Result []map[string]interface{}, SortBy string, ascending bool) {
  356. orderBy := ""
  357. switch SortBy {
  358. case "updated_unix.keyword":
  359. orderBy = "updated_unix"
  360. case "num_stars":
  361. orderBy = "num_stars"
  362. case "num_forks":
  363. orderBy = "num_forks"
  364. case "num_watches":
  365. orderBy = "num_watches"
  366. }
  367. sort.Slice(Result, func(i, j int) bool {
  368. return getInt(Result[i][orderBy], orderBy) > getInt(Result[j][orderBy], orderBy)
  369. })
  370. }
  371. func getInt(tmp interface{}, orderBy string) int64 {
  372. timeInt, err := strconv.ParseInt(fmt.Sprint(tmp), 10, 64)
  373. if err == nil {
  374. return timeInt
  375. } else {
  376. log.Info("convert " + orderBy + " error type=" + fmt.Sprint(tmp))
  377. }
  378. return -1
  379. }
  380. func makePrivateRepo(repos models.RepositoryList, res *SearchRes, keyword string, language string) {
  381. for _, repo := range repos {
  382. record := make(map[string]interface{})
  383. record["id"] = repo.ID
  384. record["name"] = makeHighLight(keyword, repo.Name)
  385. record["real_name"] = repo.Name
  386. record["owner_name"] = repo.OwnerName
  387. record["description"] = truncLongText(makeHighLight(keyword, repo.Description), true)
  388. hightTopics := make([]string, 0)
  389. if len(repo.Topics) > 0 {
  390. for _, t := range repo.Topics {
  391. hightTopics = append(hightTopics, makeHighLight(keyword, t))
  392. }
  393. }
  394. record["hightTopics"] = hightTopics
  395. record["num_watches"] = repo.NumWatches
  396. record["num_stars"] = repo.NumStars
  397. record["num_forks"] = repo.NumForks
  398. record["alias"] = truncLongText(makeHighLight(keyword, repo.Alias), true)
  399. record["lower_alias"] = repo.LowerAlias
  400. record["topics"] = repo.Topics
  401. record["avatar"] = repo.RelAvatarLink()
  402. if len(repo.RelAvatarLink()) == 0 {
  403. record["avatar"] = setting.RepositoryAvatarFallbackImage
  404. }
  405. record["updated_unix"] = repo.UpdatedUnix
  406. record["updated_html"] = timeutil.TimeSinceUnix(repo.UpdatedUnix, language)
  407. lang, err := repo.GetTopLanguageStats(1)
  408. if err == nil && len(lang) > 0 {
  409. record["lang"] = lang[0].Language
  410. } else {
  411. record["lang"] = ""
  412. }
  413. record["is_private"] = true
  414. res.Result = append(res.Result, record)
  415. }
  416. }
  417. func makeHighLight(keyword string, dest string) string {
  418. dest = replaceIngoreUpperOrLower(dest, strings.ToLower(dest), strings.ToLower(keyword))
  419. return dest
  420. }
  421. func replaceIngoreUpperOrLower(dest string, destLower string, keywordLower string) string {
  422. re := ""
  423. last := 0
  424. lenDestLower := len(destLower)
  425. lenkeywordLower := len(keywordLower)
  426. for i := 0; i < lenDestLower; i++ {
  427. if destLower[i] == keywordLower[0] {
  428. isFind := true
  429. for j := 1; j < lenkeywordLower; j++ {
  430. if (i+j) < lenDestLower && keywordLower[j] != destLower[i+j] {
  431. isFind = false
  432. break
  433. }
  434. }
  435. if isFind && (i+lenkeywordLower) <= lenDestLower {
  436. re += dest[last:i] + "\u003cfont color='red'\u003e" + dest[i:(i+lenkeywordLower)] + "\u003c/font\u003e"
  437. i = i + lenkeywordLower
  438. last = i
  439. }
  440. }
  441. }
  442. if last < lenDestLower {
  443. re += dest[last:lenDestLower]
  444. }
  445. return re
  446. }
  447. func makeRepoResult(sRes *elastic.SearchResult, Key string, OnlyReturnNum bool, language string) *SearchRes {
  448. total := sRes.Hits.TotalHits.Value
  449. result := make([]map[string]interface{}, 0)
  450. if !OnlyReturnNum {
  451. for i, hit := range sRes.Hits.Hits {
  452. log.Info("this is repo query " + fmt.Sprint(i) + " result.")
  453. recordSource := make(map[string]interface{})
  454. source, err := hit.Source.MarshalJSON()
  455. if err == nil {
  456. err = json.Unmarshal(source, &recordSource)
  457. if err == nil {
  458. record := make(map[string]interface{})
  459. record["id"] = hit.Id
  460. record["alias"] = getLabelValue("alias", recordSource, hit.Highlight)
  461. record["real_name"] = recordSource["name"]
  462. record["owner_name"] = recordSource["owner_name"]
  463. if recordSource["description"] != nil {
  464. desc := getLabelValue("description", recordSource, hit.Highlight)
  465. record["description"] = dealLongText(desc, Key, hit.MatchedQueries)
  466. } else {
  467. record["description"] = ""
  468. }
  469. record["hightTopics"] = jsonStrToArray(getLabelValue("topics", recordSource, hit.Highlight))
  470. record["num_watches"] = recordSource["num_watches"]
  471. record["num_stars"] = recordSource["num_stars"]
  472. record["num_forks"] = recordSource["num_forks"]
  473. record["lower_alias"] = recordSource["lower_alias"]
  474. record["fork_id"] = recordSource["fork_id"]
  475. if recordSource["topics"] != nil {
  476. topicsStr := recordSource["topics"].(string)
  477. log.Info("topicsStr=" + topicsStr)
  478. if topicsStr != "null" {
  479. record["topics"] = jsonStrToArray(topicsStr)
  480. }
  481. }
  482. if recordSource["avatar"] != nil {
  483. avatarstr := recordSource["avatar"].(string)
  484. if len(avatarstr) == 0 {
  485. // record["avatar"] = setting.RepositoryAvatarFallbackImage
  486. } else {
  487. record["avatar"] = setting.AppSubURL + "/repo-avatars/" + avatarstr
  488. }
  489. }
  490. record["updated_unix"] = recordSource["updated_unix"]
  491. setUpdateHtml(record, recordSource["updated_unix"].(string), language)
  492. record["lang"] = recordSource["lang"]
  493. record["is_private"] = false
  494. result = append(result, record)
  495. } else {
  496. log.Info("deal repo source error," + err.Error())
  497. }
  498. } else {
  499. log.Info("deal repo source error," + err.Error())
  500. }
  501. }
  502. }
  503. returnObj := &SearchRes{
  504. Total: total,
  505. Result: result,
  506. }
  507. return returnObj
  508. }
  509. func setUpdateHtml(record map[string]interface{}, updated_unix string, language string) {
  510. timeInt, err := strconv.ParseInt(updated_unix, 10, 64)
  511. if err == nil {
  512. record["updated_html"] = timeutil.TimeSinceUnix(timeutil.TimeStamp(timeInt), language)
  513. }
  514. }
  515. func jsonStrToArray(str string) []string {
  516. b := []byte(str)
  517. strs := make([]string, 0)
  518. err := json.Unmarshal(b, &strs)
  519. if err != nil {
  520. log.Info("convert str arrar error, str=" + str)
  521. }
  522. return strs
  523. }
  524. func dealLongText(text string, Key string, MatchedQueries []string) string {
  525. var isNeedToDealText bool
  526. isNeedToDealText = false
  527. if len(MatchedQueries) > 0 && Key != "" {
  528. if MatchedQueries[0] == "f_second" || MatchedQueries[0] == "f_third" {
  529. isNeedToDealText = true
  530. }
  531. }
  532. return truncLongText(text, isNeedToDealText)
  533. }
  534. func truncLongText(text string, isNeedToDealText bool) string {
  535. startStr := "color="
  536. textRune := []rune(text)
  537. stringlen := len(textRune)
  538. if isNeedToDealText && stringlen > 200 {
  539. index := findFont(textRune, []rune(startStr))
  540. if index > 0 {
  541. start := index - 50
  542. if start < 0 {
  543. start = 0
  544. }
  545. end := index + 150
  546. if end >= stringlen {
  547. end = stringlen
  548. }
  549. return trimFontHtml(textRune[start:end]) + "..."
  550. } else {
  551. return trimFontHtml(textRune[0:200]) + "..."
  552. }
  553. } else {
  554. if stringlen > 200 {
  555. return trimFontHtml(textRune[0:200]) + "..."
  556. } else {
  557. return text
  558. }
  559. }
  560. }
  561. func trimFontHtml(text []rune) string {
  562. startRune := rune('<')
  563. endRune := rune('>')
  564. count := 0
  565. i := 0
  566. for ; i < len(text); i++ {
  567. if text[i] == startRune { //start <
  568. re := false
  569. j := i + 1
  570. for ; j < len(text); j++ {
  571. if text[j] == endRune {
  572. re = true
  573. break
  574. }
  575. }
  576. if re { //found >
  577. i = j
  578. count++
  579. } else {
  580. if count%2 == 1 {
  581. return string(text[0:i]) + "</font>"
  582. } else {
  583. return string(text[0:i])
  584. }
  585. }
  586. }
  587. }
  588. if count%2 == 1 {
  589. return string(text[0:i]) + "</font>"
  590. } else {
  591. return string(text[0:i])
  592. }
  593. }
  594. func trimHrefHtml(result string) string {
  595. result = strings.Replace(result, "</a>", "", -1)
  596. result = strings.Replace(result, "\n", "", -1)
  597. var index int
  598. for {
  599. index = findSubstr(result, 0, "<a")
  600. if index != -1 {
  601. sIndex := findSubstr(result, index+2, ">")
  602. if sIndex != -1 {
  603. result = result[0:index] + result[sIndex+1:]
  604. } else {
  605. result = result[0:index] + result[index+2:]
  606. }
  607. } else {
  608. break
  609. }
  610. }
  611. return result
  612. }
  613. func findFont(text []rune, childText []rune) int {
  614. for i := 0; i < len(text); i++ {
  615. if text[i] == childText[0] {
  616. re := true
  617. for j, k := range childText {
  618. if k != text[i+j] {
  619. re = false
  620. break
  621. }
  622. }
  623. if re {
  624. return i
  625. }
  626. }
  627. }
  628. return -1
  629. }
  630. func findSubstr(text string, startindex int, childText string) int {
  631. for i := startindex; i < len(text); i++ {
  632. if text[i] == childText[0] {
  633. re := true
  634. for k := range childText {
  635. if childText[k] != text[i+k] {
  636. re = false
  637. break
  638. }
  639. }
  640. if re {
  641. return i
  642. }
  643. }
  644. }
  645. return -1
  646. }
  647. func searchUserOrOrg(ctx *context.Context, TableName string, Key string, Page int, PageSize int, IsQueryUser bool, OnlyReturnNum bool) {
  648. /*
  649. 用户或者组织 ES名称: user-es-index
  650. 搜索:
  651. name , 名称
  652. full_name 全名
  653. description 描述或者简介
  654. 排序:
  655. created_unix
  656. 名称字母序
  657. */
  658. SortBy := ctx.Query("SortBy")
  659. ascending := ctx.QueryBool("Ascending")
  660. boolQ := elastic.NewBoolQuery()
  661. typeValue := 1
  662. if IsQueryUser {
  663. typeValue = 0
  664. }
  665. UserOrOrgQuery := elastic.NewTermQuery("type", typeValue)
  666. if Key != "" {
  667. boolKeyQ := elastic.NewBoolQuery()
  668. log.Info("user or org Key=" + Key)
  669. nameQuery := elastic.NewMatchQuery("name", Key).Boost(2).QueryName("f_first")
  670. full_nameQuery := elastic.NewMatchQuery("full_name", Key).Boost(1.5).QueryName("f_second")
  671. descriptionQuery := elastic.NewMatchQuery("description", Key).Boost(1).QueryName("f_third")
  672. boolKeyQ.Should(nameQuery, full_nameQuery, descriptionQuery)
  673. boolQ.Must(UserOrOrgQuery, boolKeyQ)
  674. } else {
  675. boolQ.Must(UserOrOrgQuery)
  676. }
  677. 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())
  678. if err == nil {
  679. searchJson, _ := json.Marshal(res)
  680. log.Info("searchJson=" + string(searchJson))
  681. result := makeUserOrOrgResult(res, Key, ctx, OnlyReturnNum)
  682. ctx.JSON(200, result)
  683. } else {
  684. log.Info("query es error," + err.Error())
  685. ctx.JSON(200, "")
  686. }
  687. }
  688. func getLabelValue(key string, recordSource map[string]interface{}, searchHighliht elastic.SearchHitHighlight) string {
  689. if value, ok := searchHighliht[key]; !ok {
  690. if recordSource[key] != nil {
  691. return recordSource[key].(string)
  692. } else {
  693. return ""
  694. }
  695. } else {
  696. return value[0]
  697. }
  698. }
  699. func makeUserOrOrgResult(sRes *elastic.SearchResult, Key string, ctx *context.Context, OnlyReturnNum bool) *SearchRes {
  700. total := sRes.Hits.TotalHits.Value
  701. result := make([]map[string]interface{}, 0)
  702. if !OnlyReturnNum {
  703. for i, hit := range sRes.Hits.Hits {
  704. log.Info("this is user query " + fmt.Sprint(i) + " result.")
  705. recordSource := make(map[string]interface{})
  706. source, err := hit.Source.MarshalJSON()
  707. if err == nil {
  708. err = json.Unmarshal(source, &recordSource)
  709. if err == nil {
  710. record := make(map[string]interface{})
  711. record["id"] = hit.Id
  712. record["name"] = getLabelValue("name", recordSource, hit.Highlight)
  713. record["real_name"] = recordSource["name"]
  714. record["full_name"] = getLabelValue("full_name", recordSource, hit.Highlight)
  715. if recordSource["description"] != nil {
  716. desc := getLabelValue("description", recordSource, hit.Highlight)
  717. record["description"] = dealLongText(desc, Key, hit.MatchedQueries)
  718. } else {
  719. record["description"] = ""
  720. }
  721. if ctx.User != nil {
  722. record["email"] = recordSource["email"]
  723. } else {
  724. record["email"] = ""
  725. }
  726. record["location"] = recordSource["location"]
  727. record["website"] = recordSource["website"]
  728. record["num_repos"] = recordSource["num_repos"]
  729. record["num_teams"] = recordSource["num_teams"]
  730. record["num_members"] = recordSource["num_members"]
  731. record["avatar"] = strings.TrimRight(setting.AppSubURL, "/") + "/user/avatar/" + recordSource["name"].(string) + "/" + strconv.Itoa(-1)
  732. record["updated_unix"] = recordSource["updated_unix"]
  733. record["created_unix"] = recordSource["created_unix"]
  734. record["add_time"] = getAddTime(recordSource["created_unix"].(string))
  735. result = append(result, record)
  736. } else {
  737. log.Info("deal user source error," + err.Error())
  738. }
  739. } else {
  740. log.Info("deal user source error," + err.Error())
  741. }
  742. }
  743. }
  744. returnObj := &SearchRes{
  745. Total: total,
  746. Result: result,
  747. }
  748. return returnObj
  749. }
  750. func getAddTime(time string) string {
  751. timeInt, err := strconv.ParseInt(time, 10, 64)
  752. if err == nil {
  753. t := timeutil.TimeStamp(timeInt)
  754. return t.FormatShort()
  755. }
  756. return ""
  757. }
  758. func searchDataSet(ctx *context.Context, TableName string, Key string, Page int, PageSize int, OnlyReturnNum bool) {
  759. /*
  760. 数据集,ES名称:dataset-es-index
  761. 搜索:
  762. title , 名称
  763. description 描述
  764. category 标签
  765. file_name 数据集文件名称
  766. 排序:
  767. download_times
  768. */
  769. log.Info("query searchdataset start")
  770. SortBy := ctx.Query("SortBy")
  771. ascending := ctx.QueryBool("Ascending")
  772. PrivateTotal := ctx.QueryInt("PrivateTotal")
  773. WebTotal := ctx.QueryInt("WebTotal")
  774. language := ctx.Query("language")
  775. if language == "" {
  776. language = "zh-CN"
  777. }
  778. from := (Page - 1) * PageSize
  779. if from == 0 {
  780. WebTotal = 0
  781. }
  782. resultObj := &SearchRes{}
  783. log.Info("WebTotal=" + fmt.Sprint(WebTotal))
  784. log.Info("PrivateTotal=" + fmt.Sprint(PrivateTotal))
  785. resultObj.Result = make([]map[string]interface{}, 0)
  786. if ctx.User != nil && (from < PrivateTotal || from == 0) {
  787. log.Info("actor is null?:" + fmt.Sprint(ctx.User == nil))
  788. datasets, count, err := models.SearchDatasetBySQL(Page, PageSize, Key, ctx.User.ID)
  789. if err != nil {
  790. ctx.JSON(200, "")
  791. return
  792. }
  793. resultObj.PrivateTotal = count
  794. datasetSize := len(datasets)
  795. if datasetSize > 0 {
  796. log.Info("Query private dataset number is:" + fmt.Sprint(datasetSize) + " count=" + fmt.Sprint(count))
  797. makePrivateDataSet(datasets, resultObj, Key, language)
  798. } else {
  799. log.Info("not found private dataset, keyword=" + Key)
  800. }
  801. if datasetSize >= PageSize {
  802. if WebTotal > 0 { //next page, not first query.
  803. resultObj.Total = int64(WebTotal)
  804. ctx.JSON(200, resultObj)
  805. return
  806. }
  807. }
  808. } else {
  809. resultObj.PrivateTotal = int64(PrivateTotal)
  810. }
  811. from = from - PrivateTotal
  812. if from < 0 {
  813. from = 0
  814. }
  815. Size := PageSize - len(resultObj.Result)
  816. boolQ := elastic.NewBoolQuery()
  817. if Key != "" {
  818. nameQuery := elastic.NewMatchQuery("title", Key).Boost(2).QueryName("f_first")
  819. descQuery := elastic.NewMatchQuery("description", Key).Boost(1.5).QueryName("f_second")
  820. fileNameQuery := elastic.NewMatchQuery("file_name", Key).Boost(1).QueryName("f_third")
  821. categoryQuery := elastic.NewMatchQuery("category", Key).Boost(1).QueryName("f_fourth")
  822. boolQ.Should(nameQuery, descQuery, categoryQuery, fileNameQuery)
  823. 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())
  824. if err == nil {
  825. searchJson, _ := json.Marshal(res)
  826. log.Info("searchJson=" + string(searchJson))
  827. esresult := makeDatasetResult(res, Key, OnlyReturnNum, language)
  828. resultObj.Total = resultObj.PrivateTotal + esresult.Total
  829. log.Info("query dataset es count=" + fmt.Sprint(esresult.Total) + " total=" + fmt.Sprint(resultObj.Total))
  830. resultObj.Result = append(resultObj.Result, esresult.Result...)
  831. ctx.JSON(200, resultObj)
  832. } else {
  833. log.Info("query es error," + err.Error())
  834. }
  835. } else {
  836. log.Info("query all datasets.")
  837. //搜索的属性要指定{"timestamp":{"unmapped_type":"date"}}
  838. res, err := client.Search(TableName).SortBy(getSort(SortBy, ascending, "updated_unix.keyword", false)...).From(from).Size(Size).Do(ctx.Req.Context())
  839. if err == nil {
  840. searchJson, _ := json.Marshal(res)
  841. log.Info("searchJson=" + string(searchJson))
  842. esresult := makeDatasetResult(res, "", OnlyReturnNum, language)
  843. resultObj.Total = resultObj.PrivateTotal + esresult.Total
  844. log.Info("query dataset es count=" + fmt.Sprint(esresult.Total) + " total=" + fmt.Sprint(resultObj.Total))
  845. resultObj.Result = append(resultObj.Result, esresult.Result...)
  846. ctx.JSON(200, resultObj)
  847. } else {
  848. log.Info("query es error," + err.Error())
  849. ctx.JSON(200, "")
  850. }
  851. }
  852. }
  853. func makePrivateDataSet(datasets []*models.Dataset, res *SearchRes, Key string, language string) {
  854. for _, dataset := range datasets {
  855. record := make(map[string]interface{})
  856. record["id"] = dataset.ID
  857. userId := dataset.UserID
  858. user, errUser := models.GetUserByID(userId)
  859. if errUser == nil {
  860. record["owerName"] = user.GetDisplayName()
  861. record["avatar"] = user.RelAvatarLink()
  862. }
  863. repo, errRepo := models.GetRepositoryByID(dataset.RepoID)
  864. if errRepo == nil {
  865. log.Info("repo_url=" + repo.FullName())
  866. record["repoUrl"] = repo.FullName()
  867. record["avatar"] = repo.RelAvatarLink()
  868. } else {
  869. log.Info("repo err=" + errRepo.Error())
  870. }
  871. record["title"] = makeHighLight(Key, dataset.Title)
  872. record["description"] = truncLongText(makeHighLight(Key, dataset.Description), true)
  873. record["category"] = dataset.Category
  874. record["task"] = dataset.Task
  875. record["download_times"] = dataset.DownloadTimes
  876. record["created_unix"] = dataset.CreatedUnix
  877. record["updated_unix"] = repo.UpdatedUnix
  878. record["updated_html"] = timeutil.TimeSinceUnix(repo.UpdatedUnix, language)
  879. res.Result = append(res.Result, record)
  880. }
  881. }
  882. func makeDatasetResult(sRes *elastic.SearchResult, Key string, OnlyReturnNum bool, language string) *SearchRes {
  883. total := sRes.Hits.TotalHits.Value
  884. result := make([]map[string]interface{}, 0)
  885. if !OnlyReturnNum {
  886. for i, hit := range sRes.Hits.Hits {
  887. log.Info("this is dataset query " + fmt.Sprint(i) + " result.")
  888. recordSource := make(map[string]interface{})
  889. source, err := hit.Source.MarshalJSON()
  890. if err == nil {
  891. err = json.Unmarshal(source, &recordSource)
  892. if err == nil {
  893. record := make(map[string]interface{})
  894. record["id"] = hit.Id
  895. userIdStr := recordSource["user_id"].(string)
  896. userId, cerr := strconv.ParseInt(userIdStr, 10, 64)
  897. if cerr == nil {
  898. user, errUser := models.GetUserByID(userId)
  899. if errUser == nil {
  900. record["owerName"] = user.GetDisplayName()
  901. record["avatar"] = user.RelAvatarLink()
  902. }
  903. }
  904. setRepoInfo(recordSource, record)
  905. record["title"] = getLabelValue("title", recordSource, hit.Highlight)
  906. record["category"] = getLabelValue("category", recordSource, hit.Highlight)
  907. if recordSource["description"] != nil {
  908. desc := getLabelValue("description", recordSource, hit.Highlight)
  909. record["description"] = dealLongText(desc, Key, hit.MatchedQueries)
  910. } else {
  911. record["description"] = ""
  912. }
  913. record["file_name"] = getDatasetFileName(getLabelValue("file_name", recordSource, hit.Highlight))
  914. record["task"] = recordSource["task"]
  915. record["download_times"] = recordSource["download_times"]
  916. record["created_unix"] = recordSource["created_unix"]
  917. setUpdateHtml(record, recordSource["updated_unix"].(string), language)
  918. result = append(result, record)
  919. } else {
  920. log.Info("deal dataset source error," + err.Error())
  921. }
  922. } else {
  923. log.Info("deal dataset source error," + err.Error())
  924. }
  925. }
  926. }
  927. returnObj := &SearchRes{
  928. Total: total,
  929. Result: result,
  930. }
  931. return returnObj
  932. }
  933. func getDatasetFileName(fileName string) string {
  934. slices := strings.Split(fileName, "-#,#-")
  935. fileName = strings.Join(slices, ", ")
  936. return fileName
  937. }
  938. func searchIssueOrPr(ctx *context.Context, TableName string, Key string, Page int, PageSize int, OnlyReturnNum bool, issueOrPr string) {
  939. /*
  940. 任务,合并请求 ES名称:issue-es-index
  941. 搜索:
  942. name character varying(255) , 标题
  943. content text, 内容
  944. comment text, 评论
  945. 排序:
  946. updated_unix
  947. */
  948. SortBy := ctx.Query("SortBy")
  949. ascending := ctx.QueryBool("Ascending")
  950. PrivateTotal := ctx.QueryInt("PrivateTotal")
  951. WebTotal := ctx.QueryInt("WebTotal")
  952. language := ctx.Query("language")
  953. if language == "" {
  954. language = "zh-CN"
  955. }
  956. from := (Page - 1) * PageSize
  957. if from == 0 {
  958. WebTotal = 0
  959. }
  960. resultObj := &SearchRes{}
  961. log.Info("WebTotal=" + fmt.Sprint(WebTotal))
  962. log.Info("PrivateTotal=" + fmt.Sprint(PrivateTotal))
  963. resultObj.Result = make([]map[string]interface{}, 0)
  964. isPull := false
  965. if issueOrPr == "t" {
  966. isPull = true
  967. }
  968. if ctx.User != nil && (from < PrivateTotal || from == 0) {
  969. log.Info("actor is null?:" + fmt.Sprint(ctx.User == nil))
  970. issues, count, err := models.SearchPrivateIssueOrPr(Page, PageSize, Key, isPull, ctx.User.ID)
  971. if err != nil {
  972. ctx.JSON(200, "")
  973. return
  974. }
  975. resultObj.PrivateTotal = count
  976. issuesSize := len(issues)
  977. if issuesSize > 0 {
  978. log.Info("Query private repo issue number is:" + fmt.Sprint(issuesSize) + " count=" + fmt.Sprint(count))
  979. makePrivateIssueOrPr(issues, resultObj, Key, language)
  980. } else {
  981. log.Info("not found private repo issue,keyword=" + Key)
  982. }
  983. if issuesSize >= PageSize {
  984. if WebTotal > 0 { //next page, not first query.
  985. resultObj.Total = int64(WebTotal)
  986. ctx.JSON(200, resultObj)
  987. return
  988. }
  989. }
  990. } else {
  991. resultObj.PrivateTotal = int64(PrivateTotal)
  992. }
  993. from = from - PrivateTotal
  994. if from < 0 {
  995. from = 0
  996. }
  997. Size := PageSize - len(resultObj.Result)
  998. boolQ := elastic.NewBoolQuery()
  999. isIssueQuery := elastic.NewTermQuery("is_pull", issueOrPr)
  1000. if Key != "" {
  1001. boolKeyQ := elastic.NewBoolQuery()
  1002. log.Info("issue Key=" + Key)
  1003. nameQuery := elastic.NewMatchQuery("name", Key).Boost(2).QueryName("f_first")
  1004. contentQuery := elastic.NewMatchQuery("content", Key).Boost(1.5).QueryName("f_second")
  1005. commentQuery := elastic.NewMatchQuery("comment", Key).Boost(1).QueryName("f_third")
  1006. boolKeyQ.Should(nameQuery, contentQuery, commentQuery)
  1007. boolQ.Must(isIssueQuery, boolKeyQ)
  1008. } else {
  1009. boolQ.Must(isIssueQuery)
  1010. }
  1011. 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())
  1012. if err == nil {
  1013. searchJson, _ := json.Marshal(res)
  1014. log.Info("searchJson=" + string(searchJson))
  1015. esresult := makeIssueResult(res, Key, OnlyReturnNum, language)
  1016. resultObj.Total = resultObj.PrivateTotal + esresult.Total
  1017. log.Info("query issue es count=" + fmt.Sprint(esresult.Total) + " total=" + fmt.Sprint(resultObj.Total))
  1018. resultObj.Result = append(resultObj.Result, esresult.Result...)
  1019. ctx.JSON(200, resultObj)
  1020. } else {
  1021. log.Info("query es error," + err.Error())
  1022. }
  1023. }
  1024. func queryHighlight(names ...string) *elastic.Highlight {
  1025. re := elastic.NewHighlight()
  1026. for i := 0; i < len(names); i++ {
  1027. field := &elastic.HighlighterField{
  1028. Name: names[i],
  1029. }
  1030. re.Fields(field)
  1031. }
  1032. re.PreTags("<font color='red'>")
  1033. re.PostTags("</font>")
  1034. return re
  1035. }
  1036. func setRepoInfo(recordSource map[string]interface{}, record map[string]interface{}) {
  1037. repoIdstr := recordSource["repo_id"].(string)
  1038. repoId, cerr := strconv.ParseInt(repoIdstr, 10, 64)
  1039. if cerr == nil {
  1040. repo, errRepo := models.GetRepositoryByID(repoId)
  1041. if errRepo == nil {
  1042. log.Info("repo_url=" + repo.FullName())
  1043. record["repoUrl"] = repo.FullName()
  1044. record["avatar"] = repo.RelAvatarLink()
  1045. } else {
  1046. log.Info("repo err=" + errRepo.Error())
  1047. }
  1048. } else {
  1049. log.Info("parse int err=" + cerr.Error())
  1050. }
  1051. }
  1052. func makePrivateIssueOrPr(issues []*models.Issue, res *SearchRes, Key string, language string) {
  1053. for _, issue := range issues {
  1054. record := make(map[string]interface{})
  1055. record["id"] = issue.ID
  1056. record["repo_id"] = issue.RepoID
  1057. repo, errRepo := models.GetRepositoryByID(issue.RepoID)
  1058. if errRepo == nil {
  1059. log.Info("repo_url=" + repo.FullName())
  1060. record["repoUrl"] = repo.FullName()
  1061. record["avatar"] = repo.RelAvatarLink()
  1062. } else {
  1063. log.Info("repo err=" + errRepo.Error())
  1064. }
  1065. record["name"] = makeHighLight(Key, issue.Title)
  1066. record["content"] = truncLongText(makeHighLight(Key, issue.Content), true)
  1067. if issue.IsPull {
  1068. pr, err1 := issue.GetPullRequest()
  1069. if err1 == nil && pr != nil {
  1070. record["pr_id"] = pr.ID
  1071. }
  1072. }
  1073. record["index"] = issue.Index
  1074. record["num_comments"] = issue.NumComments
  1075. record["is_closed"] = issue.IsClosed
  1076. record["updated_unix"] = issue.UpdatedUnix
  1077. record["updated_html"] = timeutil.TimeSinceUnix(issue.UpdatedUnix, language)
  1078. res.Result = append(res.Result, record)
  1079. }
  1080. }
  1081. func makeIssueResult(sRes *elastic.SearchResult, Key string, OnlyReturnNum bool, language string) *SearchRes {
  1082. total := sRes.Hits.TotalHits.Value
  1083. result := make([]map[string]interface{}, 0)
  1084. if !OnlyReturnNum {
  1085. for i, hit := range sRes.Hits.Hits {
  1086. log.Info("this is issue query " + fmt.Sprint(i) + " result.")
  1087. recordSource := make(map[string]interface{})
  1088. source, err := hit.Source.MarshalJSON()
  1089. if err == nil {
  1090. err = json.Unmarshal(source, &recordSource)
  1091. if err == nil {
  1092. record := make(map[string]interface{})
  1093. record["id"] = hit.Id
  1094. record["repo_id"] = recordSource["repo_id"]
  1095. log.Info("recordSource[\"repo_id\"]=" + fmt.Sprint(recordSource["repo_id"]))
  1096. setRepoInfo(recordSource, record)
  1097. record["name"] = getLabelValue("name", recordSource, hit.Highlight)
  1098. if recordSource["content"] != nil {
  1099. desc := getLabelValue("content", recordSource, hit.Highlight)
  1100. record["content"] = dealLongText(desc, Key, hit.MatchedQueries)
  1101. if _, ok := hit.Highlight["content"]; !ok {
  1102. if _, ok_comment := hit.Highlight["comment"]; ok_comment {
  1103. desc := getLabelValue("comment", recordSource, hit.Highlight)
  1104. record["content"] = trimHrefHtml(dealLongText(desc, Key, hit.MatchedQueries))
  1105. }
  1106. }
  1107. } else {
  1108. if recordSource["comment"] != nil {
  1109. desc := getLabelValue("comment", recordSource, hit.Highlight)
  1110. record["content"] = dealLongText(desc, Key, hit.MatchedQueries)
  1111. }
  1112. }
  1113. if recordSource["pr_id"] != nil {
  1114. record["pr_id"] = recordSource["pr_id"]
  1115. }
  1116. log.Info("index=" + recordSource["index"].(string))
  1117. record["index"] = recordSource["index"]
  1118. record["num_comments"] = recordSource["num_comments"]
  1119. record["is_closed"] = recordSource["is_closed"]
  1120. record["updated_unix"] = recordSource["updated_unix"]
  1121. setUpdateHtml(record, recordSource["updated_unix"].(string), language)
  1122. result = append(result, record)
  1123. } else {
  1124. log.Info("deal issue source error," + err.Error())
  1125. }
  1126. } else {
  1127. log.Info("deal issue source error," + err.Error())
  1128. }
  1129. }
  1130. }
  1131. returnObj := &SearchRes{
  1132. Total: total,
  1133. Result: result,
  1134. }
  1135. return returnObj
  1136. }