╭ changed ───────────────╮ │src/event/Discussion.zig│ ╰────────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭─────────────────────────────────────────╮ │12 event: Self, │ │13 removed: bool = false, │ │14 author_email: ?[]const u8 = null,│ │15 - created_ts: u64 = 0, │ │15 + created_order: u64 = 0, │ │16 }; │ │17 │ │18 const Self = @This(); │ ╰─────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────╮ │24 pub const id_set_key = "discussion-id-set"; │ │25 pub const active_id_set_key = "active-discussion-id-set"; │ │26 pub const tag_to_id_set_key = "tag->discussion-id-set"; │ │27 +const activity_order_key = "discussion-id->activity-order"; │ │28 │ │29 pub fn tagIterator(tags: []const u8) std.mem.SplitIterator(u8, .scalar) {│ │30 return std.mem.splitScalar(u8, tags, ' '); │ ╰─────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │52 const records = try DB.HashMap(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, record_map_key))); │ │53 const active = try DB.SortedSet(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, active_id_set_key)));│ │54 const tags = try DB.SortedMap(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, tag_to_id_set_key))); │ │54 - │ │55 var existing_record_maybe: ?Record = null; │ │56 const existing_cursor_maybe = try records.getCursor(discussion_key); │ │57 if (existing_cursor_maybe) |existing_cursor| { │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────╮ │67 }; │ │68 │ │69 if (existing_record_maybe) |existing| { │ │70 - record_to_write.created_ts = existing.created_ts; │ │70 + record_to_write.created_order = existing.created_order; │ │71 record_to_write.author_email = existing.author_email; │ │72 + } │ │73 + │ │74 + const activity_order = if (existing_record_maybe != null) │ │75 + try activityOrder(DB, hash_kind, haxy_moment.readOnly(), event_id) │ │76 + else │ │77 + record_to_write.created_order; │ │78 + if (existing_record_maybe) |existing| { │ │79 if (!existing.removed) { │ │73 - const order_key = evt.orderKeyDesc(existing.created_ts, event_id);│ │74 - _ = try active.remove(&order_key); │ │75 - try removeFromTagSets(DB, tags, existing.event.tags, &order_key); │ │80 + const old_key = evt.orderKeyDesc(activity_order, event_id); │ │81 + _ = try active.remove(&old_key); │ │82 + try removeFromTagSets(DB, tags, existing.event.tags, &old_key); │ │83 } │ │84 } │ │85 │ ╰──────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │86 const record = try DB.HashMap(.read_write).init(try records.putCursor(discussion_key)); │ │87 try evt.upsert(Record, DB, hash_kind, record, record_to_write); │ │81 - try evt.indexEvent(DB, hash_kind, haxy_moment, event_id, .discuss, record_to_write.created_ts, record_to_write.removed); │ │88 + try evt.indexEvent(DB, hash_kind, haxy_moment, event_id, .discuss, record_to_write.created_order, record_to_write.removed); │ │89 │ │83 - const order_key = evt.orderKeyDesc(record_to_write.created_ts, event_id); │ │90 + const order_key = evt.orderKeyDesc(activity_order, event_id); │ │91 if (existing_cursor_maybe == null) { │ │92 + const activity = try DB.HashMap(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, activity_order_key)));│ │93 + try activity.put(discussion_key, .{ .uint = activity_order }); │ │94 const ids = try DB.SortedSet(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, id_set_key))); │ │86 - try ids.put(&order_key); │ │95 + try ids.put(&evt.orderKeyDesc(record_to_write.created_order, event_id)); │ │96 } │ │97 │ │98 if (!record_to_write.removed) { │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 99 try active.put(&order_key); │ │ 91 - var tag_iter = tagIterator(record_to_write.event.tags); │ │ 92 - while (tag_iter.next()) |tag| { │ │ 93 - if (tag.len == 0 or tag.len > tag_max_len) continue; │ │ 94 - const set = try DB.SortedSet(.read_write).init(try tags.putCursor(tag)); │ │ 95 - try set.put(&order_key); │ │ 96 - } │ │100 + try addToTagSets(DB, tags, record_to_write.event.tags, &order_key); │ │101 + } │ │102 +} │ │103 + │ │104 +pub fn touch( │ │105 + comptime DB: type, │ │106 + comptime hash_kind: hash.HashKind, │ │107 + haxy_moment: DB.HashMap(.read_write), │ │108 + event_id: *const [evt.event_id_size]u8, │ │109 + order: u64, │ │110 + arena: *std.heap.ArenaAllocator, │ │111 +) !void { │ │112 + const discussion_key = hash.hashInt(hash_kind, event_id); │ │113 + const records_cursor = try haxy_moment.getCursor(hash.hashInt(hash_kind, record_map_key)) orelse return; │ │114 + const records = try DB.HashMap(.read_only).init(records_cursor); │ │115 + const record_cursor = try records.getCursor(discussion_key) orelse return; │ │116 + const record = try evt.read(Record, DB, hash_kind, arena, try DB.HashMap(.read_only).init(record_cursor)); │ │117 + const current = try activityOrder(DB, hash_kind, haxy_moment.readOnly(), event_id); │ │118 + if (order <= current) return; │ │119 + │ │120 + const active = try DB.SortedSet(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, active_id_set_key))); │ │121 + const tags = try DB.SortedMap(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, tag_to_id_set_key))); │ │122 + if (!record.removed) { │ │123 + const old_key = evt.orderKeyDesc(current, event_id); │ │124 + _ = try active.remove(&old_key); │ │125 + try removeFromTagSets(DB, tags, record.event.tags, &old_key); │ │126 + } │ │127 + │ │128 + const activity = try DB.HashMap(.read_write).init(try haxy_moment.putCursor(hash.hashInt(hash_kind, activity_order_key))); │ │129 + try activity.put(discussion_key, .{ .uint = order }); │ │130 + if (!record.removed) { │ │131 + const new_key = evt.orderKeyDesc(order, event_id); │ │132 + try active.put(&new_key); │ │133 + try addToTagSets(DB, tags, record.event.tags, &new_key); │ │134 } │ │135 +} │ │136 + │ │137 +pub fn activityOrder( │ │138 + comptime DB: type, │ │139 + comptime hash_kind: hash.HashKind, │ │140 + haxy_moment: DB.HashMap(.read_only), │ │141 + event_id: *const [evt.event_id_size]u8, │ │142 +) !u64 { │ │143 + const map_cursor = try haxy_moment.getCursor(hash.hashInt(hash_kind, activity_order_key)) orelse return error.CursorNotFound;│ │144 + const map = try DB.HashMap(.read_only).init(map_cursor); │ │145 + const order_cursor = try map.getCursor(hash.hashInt(hash_kind, event_id)) orelse return error.CursorNotFound; │ │146 + return try order_cursor.readUint(); │ │147 } │ │148 │ │149 pub fn update( │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │188 } │ │189 } │ │190 │ │191 +fn addToTagSets(comptime DB: type, tags: DB.SortedMap(.read_write), names: []const u8, order_key: []const u8) !void {│ │192 + var tag_iter = tagIterator(names); │ │193 + while (tag_iter.next()) |tag| { │ │194 + if (tag.len == 0 or tag.len > tag_max_len) continue; │ │195 + const set = try DB.SortedSet(.read_write).init(try tags.putCursor(tag)); │ │196 + try set.put(order_key); │ │197 + } │ │198 +} │ │199 + │ │200 pub fn readById( │ │201 comptime repo_kind: rp.RepoKind, │ │202 comptime repo_opts: rp.RepoOpts(repo_kind), │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯