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

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