You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

queries.go 2.6KB

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