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

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