search_builder.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. package sqlstore
  2. import (
  3. "strings"
  4. m "github.com/grafana/grafana/pkg/models"
  5. )
  6. // SearchBuilder is a builder/object mother that builds a dashboard search query
  7. type SearchBuilder struct {
  8. SqlBuilder
  9. tags []string
  10. isStarred bool
  11. limit int64
  12. page int64
  13. signedInUser *m.SignedInUser
  14. whereDashboardIdsIn []int64
  15. whereTitle string
  16. whereTypeFolder bool
  17. whereTypeDash bool
  18. whereFolderIds []int64
  19. permission m.PermissionType
  20. }
  21. func NewSearchBuilder(signedInUser *m.SignedInUser, limit int64, page int64, permission m.PermissionType) *SearchBuilder {
  22. // Default to page 1
  23. if page < 1 {
  24. page = 1
  25. }
  26. // default limit
  27. if limit <= 0 {
  28. limit = 1000
  29. }
  30. searchBuilder := &SearchBuilder{
  31. signedInUser: signedInUser,
  32. limit: limit,
  33. page: page,
  34. permission: permission,
  35. }
  36. return searchBuilder
  37. }
  38. func (sb *SearchBuilder) WithTags(tags []string) *SearchBuilder {
  39. if len(tags) > 0 {
  40. sb.tags = tags
  41. }
  42. return sb
  43. }
  44. func (sb *SearchBuilder) IsStarred() *SearchBuilder {
  45. sb.isStarred = true
  46. return sb
  47. }
  48. func (sb *SearchBuilder) WithDashboardIdsIn(ids []int64) *SearchBuilder {
  49. if len(ids) > 0 {
  50. sb.whereDashboardIdsIn = ids
  51. }
  52. return sb
  53. }
  54. func (sb *SearchBuilder) WithTitle(title string) *SearchBuilder {
  55. sb.whereTitle = title
  56. return sb
  57. }
  58. func (sb *SearchBuilder) WithType(queryType string) *SearchBuilder {
  59. if len(queryType) > 0 && queryType == "dash-folder" {
  60. sb.whereTypeFolder = true
  61. }
  62. if len(queryType) > 0 && queryType == "dash-db" {
  63. sb.whereTypeDash = true
  64. }
  65. return sb
  66. }
  67. func (sb *SearchBuilder) WithFolderIds(folderIds []int64) *SearchBuilder {
  68. sb.whereFolderIds = folderIds
  69. return sb
  70. }
  71. // ToSql builds the sql and returns it as a string, together with the params.
  72. func (sb *SearchBuilder) ToSql() (string, []interface{}) {
  73. sb.params = make([]interface{}, 0)
  74. sb.buildSelect()
  75. if len(sb.tags) > 0 {
  76. sb.buildTagQuery()
  77. } else {
  78. sb.buildMainQuery()
  79. }
  80. sb.sql.WriteString(`
  81. ORDER BY dashboard.id ` + dialect.LimitOffset(sb.limit, (sb.page-1)*sb.limit) + `) as ids
  82. INNER JOIN dashboard on ids.id = dashboard.id
  83. `)
  84. sb.sql.WriteString(`
  85. LEFT OUTER JOIN dashboard folder on folder.id = dashboard.folder_id
  86. LEFT OUTER JOIN dashboard_tag on dashboard.id = dashboard_tag.dashboard_id`)
  87. sb.sql.WriteString(" ORDER BY dashboard.title ASC")
  88. return sb.sql.String(), sb.params
  89. }
  90. func (sb *SearchBuilder) buildSelect() {
  91. sb.sql.WriteString(
  92. `SELECT
  93. dashboard.id,
  94. dashboard.uid,
  95. dashboard.title,
  96. dashboard.slug,
  97. dashboard_tag.term,
  98. dashboard.is_folder,
  99. dashboard.folder_id,
  100. folder.uid as folder_uid,
  101. folder.slug as folder_slug,
  102. folder.title as folder_title
  103. FROM `)
  104. }
  105. func (sb *SearchBuilder) buildTagQuery() {
  106. sb.sql.WriteString(
  107. `(
  108. SELECT
  109. dashboard.id FROM dashboard
  110. LEFT OUTER JOIN dashboard_tag ON dashboard_tag.dashboard_id = dashboard.id
  111. `)
  112. if sb.isStarred {
  113. sb.sql.WriteString(" INNER JOIN star on star.dashboard_id = dashboard.id")
  114. }
  115. sb.sql.WriteString(` WHERE dashboard_tag.term IN (?` + strings.Repeat(",?", len(sb.tags)-1) + `) AND `)
  116. for _, tag := range sb.tags {
  117. sb.params = append(sb.params, tag)
  118. }
  119. sb.buildSearchWhereClause()
  120. // this ends the inner select (tag filtered part)
  121. sb.sql.WriteString(` GROUP BY dashboard.id HAVING COUNT(dashboard.id) >= ? `)
  122. sb.params = append(sb.params, len(sb.tags))
  123. }
  124. func (sb *SearchBuilder) buildMainQuery() {
  125. sb.sql.WriteString(`( SELECT dashboard.id FROM dashboard `)
  126. if sb.isStarred {
  127. sb.sql.WriteString(" INNER JOIN star on star.dashboard_id = dashboard.id")
  128. }
  129. sb.sql.WriteString(` WHERE `)
  130. sb.buildSearchWhereClause()
  131. }
  132. func (sb *SearchBuilder) buildSearchWhereClause() {
  133. sb.sql.WriteString(` dashboard.org_id=?`)
  134. sb.params = append(sb.params, sb.signedInUser.OrgId)
  135. if sb.isStarred {
  136. sb.sql.WriteString(` AND star.user_id=?`)
  137. sb.params = append(sb.params, sb.signedInUser.UserId)
  138. }
  139. if len(sb.whereDashboardIdsIn) > 0 {
  140. sb.sql.WriteString(` AND dashboard.id IN (?` + strings.Repeat(",?", len(sb.whereDashboardIdsIn)-1) + `)`)
  141. for _, dashboardId := range sb.whereDashboardIdsIn {
  142. sb.params = append(sb.params, dashboardId)
  143. }
  144. }
  145. sb.writeDashboardPermissionFilter(sb.signedInUser, sb.permission)
  146. if len(sb.whereTitle) > 0 {
  147. sb.sql.WriteString(" AND dashboard.title " + dialect.LikeStr() + " ?")
  148. sb.params = append(sb.params, "%"+sb.whereTitle+"%")
  149. }
  150. if sb.whereTypeFolder {
  151. sb.sql.WriteString(" AND dashboard.is_folder = " + dialect.BooleanStr(true))
  152. }
  153. if sb.whereTypeDash {
  154. sb.sql.WriteString(" AND dashboard.is_folder = " + dialect.BooleanStr(false))
  155. }
  156. if len(sb.whereFolderIds) > 0 {
  157. sb.sql.WriteString(` AND dashboard.folder_id IN (?` + strings.Repeat(",?", len(sb.whereFolderIds)-1) + `) `)
  158. for _, id := range sb.whereFolderIds {
  159. sb.params = append(sb.params, id)
  160. }
  161. }
  162. }