1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# Each card's place in a list (Contact#sort_key), so a group's first
# few members by that order are a query rather than the whole group
# built and sorted to take them from — `sync:*` holds every card, and
# a contact's tag for it names five
# (docs/plans/2026-10-22-perceived-speed.md). Index, like
# search_keys: filled through the app's own Contact#sort_key, since
# what it must agree with is the order a list sorts in. A group lends
# no name, so the stored card alone decides it.
require "pro_tacts/contact"
require "pro_tacts/vcard"
Sequel.migration do
up do
create_table(:sort_keys, strict: true) do
foreign_key :card_id, :cards, type: String, text: true, primary_key: true, null: false, on_delete: :cascade
String :sort_key, text: true, null: false
index [:sort_key, :card_id]
end
self[:cards].order(:id).each do |row|
id = row.fetch(:id)
contact = ProTacts::Contact.new(id:, stored: ProTacts::VCard.new(row.fetch(:vcard)), birthday: nil, inherited: [])
self[:sort_keys].insert(card_id: id, sort_key: contact.sort_key)
end
end
down do
drop_table(:sort_keys)
end
end