Skip to content

Instantly share code, notes, and snippets.

@jqr
Created February 22, 2024 13:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jqr/8c2780fdb14cedb8801c7469d5457355 to your computer and use it in GitHub Desktop.
Save jqr/8c2780fdb14cedb8801c7469d5457355 to your computer and use it in GitHub Desktop.

Cursoring Constraints

Exploring cursoring constraints with multiple field ordering. While Ruby's Array#<=> operator does this internally, this was about seeing the internal constraints to port them to various database systems.

Makes all combinations of a=1..3 b=1..3 c=1..3 in a pre-sorted order. Array slicing operations are then used to validate the instance methods results against this ground truth.

Iterating through the array of all Things, we test before?, after?, and the inclusive variations and print a nice table.

require "pastel"
require "tty-table"
$pastel = Pastel.new(enabled: true)
class Thing
attr_reader :a, :b, :c
def initialize(a, b, c)
@a, @b, @c = a, b, c
end
def <=>(other)
cursor <=> other.cursor
end
def cursor
[a, b, c]
end
def after?(a, b, c, inclusive: false)
(inclusive && [a, b, c] == cursor) ||
(self.a == a && self.b == b && self.c > c) ||
(self.a == a && self.b > b) ||
(self.a > a)
end
def before?(a, b, c, inclusive: false)
(inclusive && [a, b, c] == cursor) ||
(self.a == a && self.b == b && self.c < c) ||
(self.a == a && self.b < b) ||
(self.a < a)
end
def inspect
"<Thing #{self}>"
end
def to_s
[a, b, c].join(".")
end
end
things = 1.upto(3).flat_map do |a|
1.upto(3).flat_map do |b|
1.upto(3).map do |c|
Thing.new(a, b, c)
end
end
end
def array_comparison(actual, expected, difference_limit: 3)
if actual == expected
$pastel.green(actual.size)
else
added = (actual - expected).map { "+#{_1}" }
missing = (expected - actual).map { "-#{_1}" }
results = added + missing
results = results.first(difference_limit) if difference_limit && results.size > difference_limit
$pastel.red(results.join(" "))
end
end
table = TTY::Table.new(header: ["", "after", "after(i)", "before", "before(i)"])
things.each.with_index do |thing, index|
name = "#{index + 1}. #{$pastel.blue(thing.inspect)}"
expected_after_things = things[(index + 1)..]
after_things = things.select { _1.after?(*thing.cursor) }
after_result = array_comparison(after_things, expected_after_things)
expected_after_things_inclusive = things[index..]
after_things_inclusive = things.select { _1.after?(*thing.cursor, inclusive: true) }
after_inclusive_result = array_comparison(after_things_inclusive, expected_after_things_inclusive)
expected_before_things = things[0, index]
before_things = things.select { _1.before?(*thing.cursor) }
before_result = array_comparison(before_things, expected_before_things)
expected_before_things_inclusive = things[0, index + 1]
before_things_inclusive = things.select { _1.before?(*thing.cursor, inclusive: true) }
before_inclusive_result = array_comparison(before_things_inclusive, expected_before_things_inclusive)
table << [name, after_result, after_inclusive_result, before_result, before_inclusive_result]
end
puts table.render(:unicode, padding: [0, 1], alignments: [:right] * 5)
┌───────────────────┬───────┬──────────┬────────┬───────────┐
│ │ after │ after(i) │ before │ before(i) │
├───────────────────┼───────┼──────────┼────────┼───────────┤
│ 1. <Thing 1.1.1> │ 26 │ 27 │ 0 │ 1 │
│ 2. <Thing 1.1.2> │ 25 │ 26 │ 1 │ 2 │
│ 3. <Thing 1.1.3> │ 24 │ 25 │ 2 │ 3 │
│ 4. <Thing 1.2.1> │ 23 │ 24 │ 3 │ 4 │
│ 5. <Thing 1.2.2> │ 22 │ 23 │ 4 │ 5 │
│ 6. <Thing 1.2.3> │ 21 │ 22 │ 5 │ 6 │
│ 7. <Thing 1.3.1> │ 20 │ 21 │ 6 │ 7 │
│ 8. <Thing 1.3.2> │ 19 │ 20 │ 7 │ 8 │
│ 9. <Thing 1.3.3> │ 18 │ 19 │ 8 │ 9 │
│ 10. <Thing 2.1.1> │ 17 │ 18 │ 9 │ 10 │
│ 11. <Thing 2.1.2> │ 16 │ 17 │ 10 │ 11 │
│ 12. <Thing 2.1.3> │ 15 │ 16 │ 11 │ 12 │
│ 13. <Thing 2.2.1> │ 14 │ 15 │ 12 │ 13 │
│ 14. <Thing 2.2.2> │ 13 │ 14 │ 13 │ 14 │
│ 15. <Thing 2.2.3> │ 12 │ 13 │ 14 │ 15 │
│ 16. <Thing 2.3.1> │ 11 │ 12 │ 15 │ 16 │
│ 17. <Thing 2.3.2> │ 10 │ 11 │ 16 │ 17 │
│ 18. <Thing 2.3.3> │ 9 │ 10 │ 17 │ 18 │
│ 19. <Thing 3.1.1> │ 8 │ 9 │ 18 │ 19 │
│ 20. <Thing 3.1.2> │ 7 │ 8 │ 19 │ 20 │
│ 21. <Thing 3.1.3> │ 6 │ 7 │ 20 │ 21 │
│ 22. <Thing 3.2.1> │ 5 │ 6 │ 21 │ 22 │
│ 23. <Thing 3.2.2> │ 4 │ 5 │ 22 │ 23 │
│ 24. <Thing 3.2.3> │ 3 │ 4 │ 23 │ 24 │
│ 25. <Thing 3.3.1> │ 2 │ 3 │ 24 │ 25 │
│ 26. <Thing 3.3.2> │ 1 │ 2 │ 25 │ 26 │
│ 27. <Thing 3.3.3> │ 0 │ 1 │ 26 │ 27 │
└───────────────────┴───────┴──────────┴────────┴───────────┘
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment