選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

queries.go 3.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. // Copyright (c) 2020 Shivaram Lingamneni <slingamn@cs.stanford.edu>
  2. // released under the MIT license
  3. package history
  4. import (
  5. "strings"
  6. "time"
  7. )
  8. // Selector represents a parameter to a CHATHISTORY command
  9. type Selector struct {
  10. Msgid string
  11. Time time.Time
  12. }
  13. // Sequence is an abstract sequence of history entries that can be queried;
  14. // it encapsulates restrictions such as registration time cutoffs, or
  15. // only looking at a single "query buffer" (DMs with a particular correspondent)
  16. type Sequence interface {
  17. Between(start, end Selector, limit int) (results []Item, err error)
  18. Around(start Selector, limit int) (results []Item, err error)
  19. ListCorrespondents(start, end Selector, limit int) (results []TargetListing, err error)
  20. // this are weird hacks that violate the encapsulation of Sequence to some extent;
  21. // Cutoff() returns the cutoff time for other code to use (it returns the zero time
  22. // if none is set), and Ephemeral() returns whether the backing store is in-memory
  23. // or a persistent database.
  24. Cutoff() time.Time
  25. Ephemeral() bool
  26. }
  27. // This is a bad, slow implementation of CHATHISTORY AROUND using the BETWEEN semantics
  28. func GenericAround(seq Sequence, start Selector, limit int) (results []Item, err error) {
  29. var halfLimit int
  30. halfLimit = (limit + 1) / 2
  31. initialResults, err := seq.Between(Selector{}, start, halfLimit)
  32. if err != nil {
  33. return
  34. } else if len(initialResults) == 0 {
  35. // TODO: this fails if we're doing an AROUND on the first message in the buffer
  36. // would be nice to fix this but whatever
  37. return
  38. }
  39. newStart := Selector{Time: initialResults[0].Message.Time}
  40. results, err = seq.Between(newStart, Selector{}, limit)
  41. return
  42. }
  43. // MinMaxAsc converts CHATHISTORY arguments into time intervals, handling the most
  44. // general case (BETWEEN going forwards or backwards) natively and the other ordering
  45. // queries (AFTER, BEFORE, LATEST) as special cases.
  46. func MinMaxAsc(after, before, cutoff time.Time) (min, max time.Time, ascending bool) {
  47. startIsZero, endIsZero := after.IsZero(), before.IsZero()
  48. if !startIsZero && endIsZero {
  49. // AFTER
  50. ascending = true
  51. } else if startIsZero && !endIsZero {
  52. // BEFORE
  53. ascending = false
  54. } else if !startIsZero && !endIsZero {
  55. if before.Before(after) {
  56. // BETWEEN going backwards
  57. before, after = after, before
  58. ascending = false
  59. } else {
  60. // BETWEEN going forwards
  61. ascending = true
  62. }
  63. } else if startIsZero && endIsZero {
  64. // LATEST
  65. ascending = false
  66. }
  67. if after.IsZero() || after.Before(cutoff) {
  68. // this may result in an impossible query, which is fine
  69. after = cutoff
  70. }
  71. return after, before, ascending
  72. }
  73. // maps regular msgids from JOIN, etc. to a msgid suitable for attaching
  74. // to a HistServ message describing the JOIN. See #491 for some history.
  75. func HistservMungeMsgid(msgid string) string {
  76. return "_" + msgid
  77. }
  78. // strips munging from a msgid. future schemes may not support a well-defined
  79. // mapping of munged msgids to true msgids, but munged msgids should always contain
  80. // a _, with metadata in front and data (possibly the true msgid) after.
  81. func NormalizeMsgid(msgid string) string {
  82. return strings.TrimPrefix(msgid, "_")
  83. }