index_pattern.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. package es
  2. import (
  3. "fmt"
  4. "regexp"
  5. "strings"
  6. "time"
  7. "github.com/grafana/grafana/pkg/tsdb"
  8. )
  9. const (
  10. noInterval = ""
  11. intervalHourly = "hourly"
  12. intervalDaily = "daily"
  13. intervalWeekly = "weekly"
  14. intervalMonthly = "monthly"
  15. intervalYearly = "yearly"
  16. )
  17. type indexPattern interface {
  18. GetIndices(timeRange *tsdb.TimeRange) ([]string, error)
  19. }
  20. var newIndexPattern = func(interval string, pattern string) (indexPattern, error) {
  21. if interval == noInterval {
  22. return &staticIndexPattern{indexName: pattern}, nil
  23. }
  24. return newDynamicIndexPattern(interval, pattern)
  25. }
  26. type staticIndexPattern struct {
  27. indexName string
  28. }
  29. func (ip *staticIndexPattern) GetIndices(timeRange *tsdb.TimeRange) ([]string, error) {
  30. return []string{ip.indexName}, nil
  31. }
  32. type intervalGenerator interface {
  33. Generate(from, to time.Time) []time.Time
  34. }
  35. type dynamicIndexPattern struct {
  36. interval string
  37. pattern string
  38. intervalGenerator intervalGenerator
  39. }
  40. func newDynamicIndexPattern(interval, pattern string) (*dynamicIndexPattern, error) {
  41. var generator intervalGenerator
  42. switch strings.ToLower(interval) {
  43. case intervalHourly:
  44. generator = &hourlyInterval{}
  45. case intervalDaily:
  46. generator = &dailyInterval{}
  47. case intervalWeekly:
  48. generator = &weeklyInterval{}
  49. case intervalMonthly:
  50. generator = &monthlyInterval{}
  51. case intervalYearly:
  52. generator = &yearlyInterval{}
  53. default:
  54. return nil, fmt.Errorf("unsupported interval '%s'", interval)
  55. }
  56. return &dynamicIndexPattern{
  57. interval: interval,
  58. pattern: pattern,
  59. intervalGenerator: generator,
  60. }, nil
  61. }
  62. func (ip *dynamicIndexPattern) GetIndices(timeRange *tsdb.TimeRange) ([]string, error) {
  63. from := timeRange.GetFromAsTimeUTC()
  64. to := timeRange.GetToAsTimeUTC()
  65. intervals := ip.intervalGenerator.Generate(from, to)
  66. indices := make([]string, 0)
  67. for _, t := range intervals {
  68. indices = append(indices, formatDate(t, ip.pattern))
  69. }
  70. return indices, nil
  71. }
  72. type hourlyInterval struct{}
  73. func (i *hourlyInterval) Generate(from, to time.Time) []time.Time {
  74. intervals := []time.Time{}
  75. start := time.Date(from.Year(), from.Month(), from.Day(), from.Hour(), 0, 0, 0, time.UTC)
  76. end := time.Date(to.Year(), to.Month(), to.Day(), to.Hour(), 0, 0, 0, time.UTC)
  77. intervals = append(intervals, start)
  78. for start.Before(end) {
  79. start = start.Add(time.Hour)
  80. intervals = append(intervals, start)
  81. }
  82. return intervals
  83. }
  84. type dailyInterval struct{}
  85. func (i *dailyInterval) Generate(from, to time.Time) []time.Time {
  86. intervals := []time.Time{}
  87. start := time.Date(from.Year(), from.Month(), from.Day(), 0, 0, 0, 0, time.UTC)
  88. end := time.Date(to.Year(), to.Month(), to.Day(), 0, 0, 0, 0, time.UTC)
  89. intervals = append(intervals, start)
  90. for start.Before(end) {
  91. start = start.Add(24 * time.Hour)
  92. intervals = append(intervals, start)
  93. }
  94. return intervals
  95. }
  96. type weeklyInterval struct{}
  97. func (i *weeklyInterval) Generate(from, to time.Time) []time.Time {
  98. intervals := []time.Time{}
  99. start := time.Date(from.Year(), from.Month(), from.Day(), 0, 0, 0, 0, time.UTC)
  100. end := time.Date(to.Year(), to.Month(), to.Day(), 0, 0, 0, 0, time.UTC)
  101. for start.Weekday() != time.Monday {
  102. start = start.Add(-24 * time.Hour)
  103. }
  104. for end.Weekday() != time.Monday {
  105. end = end.Add(-24 * time.Hour)
  106. }
  107. year, week := start.ISOWeek()
  108. intervals = append(intervals, start)
  109. for start.Before(end) {
  110. start = start.Add(24 * time.Hour)
  111. nextYear, nextWeek := start.ISOWeek()
  112. if nextYear != year || nextWeek != week {
  113. intervals = append(intervals, start)
  114. }
  115. year = nextYear
  116. week = nextWeek
  117. }
  118. return intervals
  119. }
  120. type monthlyInterval struct{}
  121. func (i *monthlyInterval) Generate(from, to time.Time) []time.Time {
  122. intervals := []time.Time{}
  123. start := time.Date(from.Year(), from.Month(), 1, 0, 0, 0, 0, time.UTC)
  124. end := time.Date(to.Year(), to.Month(), 1, 0, 0, 0, 0, time.UTC)
  125. month := start.Month()
  126. intervals = append(intervals, start)
  127. for start.Before(end) {
  128. start = start.Add(24 * time.Hour)
  129. nextMonth := start.Month()
  130. if nextMonth != month {
  131. intervals = append(intervals, start)
  132. }
  133. month = nextMonth
  134. }
  135. return intervals
  136. }
  137. type yearlyInterval struct{}
  138. func (i *yearlyInterval) Generate(from, to time.Time) []time.Time {
  139. intervals := []time.Time{}
  140. start := time.Date(from.Year(), 1, 1, 0, 0, 0, 0, time.UTC)
  141. end := time.Date(to.Year(), 1, 1, 0, 0, 0, 0, time.UTC)
  142. year := start.Year()
  143. intervals = append(intervals, start)
  144. for start.Before(end) {
  145. start = start.Add(24 * time.Hour)
  146. nextYear := start.Year()
  147. if nextYear != year {
  148. intervals = append(intervals, start)
  149. }
  150. year = nextYear
  151. }
  152. return intervals
  153. }
  154. var datePatternRegex = regexp.MustCompile("(LT|LL?L?L?|l{1,4}|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|SS?S?|X|zz?|ZZ?|Q)")
  155. var datePatternReplacements = map[string]string{
  156. "M": "1", // stdNumMonth 1 2 ... 11 12
  157. "MM": "01", // stdZeroMonth 01 02 ... 11 12
  158. "MMM": "Jan", // stdMonth Jan Feb ... Nov Dec
  159. "MMMM": "January", // stdLongMonth January February ... November December
  160. "D": "2", // stdDay 1 2 ... 30 30
  161. "DD": "02", // stdZeroDay 01 02 ... 30 31
  162. "DDD": "<stdDayOfYear>", // Day of the year 1 2 ... 364 365
  163. "DDDD": "<stdDayOfYearZero>", // Day of the year 001 002 ... 364 365 @todo****
  164. "d": "<stdDayOfWeek>", // Numeric representation of day of the week 0 1 ... 5 6
  165. "dd": "Mon", // ***Su Mo ... Fr Sa @todo
  166. "ddd": "Mon", // Sun Mon ... Fri Sat
  167. "dddd": "Monday", // stdLongWeekDay Sunday Monday ... Friday Saturday
  168. "e": "<stdDayOfWeek>", // Numeric representation of day of the week 0 1 ... 5 6 @todo
  169. "E": "<stdDayOfWeekISO>", // ISO-8601 numeric representation of the day of the week (added in PHP 5.1.0) 1 2 ... 6 7 @todo
  170. "w": "<stdWeekOfYear>", // 1 2 ... 52 53
  171. "ww": "<stdWeekOfYear>", // ***01 02 ... 52 53 @todo
  172. "W": "<stdWeekOfYear>", // 1 2 ... 52 53
  173. "WW": "<stdWeekOfYear>", // ***01 02 ... 52 53 @todo
  174. "YY": "06", // stdYear 70 71 ... 29 30
  175. "YYYY": "2006", // stdLongYear 1970 1971 ... 2029 2030
  176. "gg": "<stdIsoYearShort>", // ISO-8601 year number 70 71 ... 29 30
  177. "gggg": "<stdIsoYear>", // ***1970 1971 ... 2029 2030
  178. "GG": "<stdIsoYearShort>", //70 71 ... 29 30
  179. "GGGG": "<stdIsoYear>", // ***1970 1971 ... 2029 2030
  180. "Q": "<stdQuarter>", // 1, 2, 3, 4
  181. "A": "PM", // stdPM AM PM
  182. "a": "pm", // stdpm am pm
  183. "H": "<stdHourNoZero>", // stdHour 0 1 ... 22 23
  184. "HH": "15", // 00 01 ... 22 23
  185. "h": "3", // stdHour12 1 2 ... 11 12
  186. "hh": "03", // stdZeroHour12 01 02 ... 11 12
  187. "m": "4", // stdZeroMinute 0 1 ... 58 59
  188. "mm": "04", // stdZeroMinute 00 01 ... 58 59
  189. "s": "5", // stdSecond 0 1 ... 58 59
  190. "ss": "05", // stdZeroSecond ***00 01 ... 58 59
  191. "z": "MST", //EST CST ... MST PST
  192. "zz": "MST", //EST CST ... MST PST
  193. "Z": "Z07:00", // stdNumColonTZ -07:00 -06:00 ... +06:00 +07:00
  194. "ZZ": "-0700", // stdNumTZ -0700 -0600 ... +0600 +0700
  195. "X": "<stdUnix>", // Seconds since unix epoch 1360013296
  196. "LT": "3:04 PM", // 8:30 PM
  197. "L": "01/02/2006", //09/04/1986
  198. "l": "1/2/2006", //9/4/1986
  199. "ll": "Jan 2 2006", //Sep 4 1986
  200. "lll": "Jan 2 2006 3:04 PM", //Sep 4 1986 8:30 PM
  201. "llll": "Mon, Jan 2 2006 3:04 PM", //Thu, Sep 4 1986 8:30 PM
  202. }
  203. func formatDate(t time.Time, pattern string) string {
  204. var datePattern string
  205. base := ""
  206. ltr := false
  207. if strings.HasPrefix(pattern, "[") {
  208. parts := strings.Split(strings.TrimLeft(pattern, "["), "]")
  209. base = parts[0]
  210. if len(parts) == 2 {
  211. datePattern = parts[1]
  212. } else {
  213. datePattern = base
  214. base = ""
  215. }
  216. ltr = true
  217. } else if strings.HasSuffix(pattern, "]") {
  218. parts := strings.Split(strings.TrimRight(pattern, "]"), "[")
  219. datePattern = parts[0]
  220. if len(parts) == 2 {
  221. base = parts[1]
  222. } else {
  223. base = ""
  224. }
  225. ltr = false
  226. }
  227. formatted := t.Format(patternToLayout(datePattern))
  228. if strings.Contains(formatted, "<std") {
  229. isoYear, isoWeek := t.ISOWeek()
  230. isoYearShort := fmt.Sprintf("%d", isoYear)[2:4]
  231. formatted = strings.Replace(formatted, "<stdIsoYear>", fmt.Sprintf("%d", isoYear), -1)
  232. formatted = strings.Replace(formatted, "<stdIsoYearShort>", isoYearShort, -1)
  233. formatted = strings.Replace(formatted, "<stdWeekOfYear>", fmt.Sprintf("%02d", isoWeek), -1)
  234. formatted = strings.Replace(formatted, "<stdUnix>", fmt.Sprintf("%d", t.Unix()), -1)
  235. day := t.Weekday()
  236. dayOfWeekIso := int(day)
  237. if day == time.Sunday {
  238. dayOfWeekIso = 7
  239. }
  240. formatted = strings.Replace(formatted, "<stdDayOfWeek>", fmt.Sprintf("%d", day), -1)
  241. formatted = strings.Replace(formatted, "<stdDayOfWeekISO>", fmt.Sprintf("%d", dayOfWeekIso), -1)
  242. formatted = strings.Replace(formatted, "<stdDayOfYear>", fmt.Sprintf("%d", t.YearDay()), -1)
  243. quarter := 4
  244. switch t.Month() {
  245. case time.January, time.February, time.March:
  246. quarter = 1
  247. case time.April, time.May, time.June:
  248. quarter = 2
  249. case time.July, time.August, time.September:
  250. quarter = 3
  251. }
  252. formatted = strings.Replace(formatted, "<stdQuarter>", fmt.Sprintf("%d", quarter), -1)
  253. formatted = strings.Replace(formatted, "<stdHourNoZero>", fmt.Sprintf("%d", t.Hour()), -1)
  254. }
  255. if ltr {
  256. return base + formatted
  257. }
  258. return formatted + base
  259. }
  260. func patternToLayout(pattern string) string {
  261. var match [][]string
  262. if match = datePatternRegex.FindAllStringSubmatch(pattern, -1); match == nil {
  263. return pattern
  264. }
  265. for i := range match {
  266. if replace, ok := datePatternReplacements[match[i][0]]; ok {
  267. pattern = strings.Replace(pattern, match[i][0], replace, 1)
  268. }
  269. }
  270. return pattern
  271. }