╭ changed ─────╮ │src/search.zig│ ╰──────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭────────────────────────────────────────────────────────────────────────────────────╮ │18 const max_expansions = 50; │ │19 // the largest doc key, so results are read into a buffer rather than allocated.│ │20 pub const max_doc_key_len = 64; │ │21 - │ │22 -// index `text` under `doc_key`. │ │23 -pub fn add( │ │24 - comptime DB: type, │ │25 - index: DB.SortedMap(.read_write), │ │26 - allocator: std.mem.Allocator, │ │27 - doc_key: []const u8, │ │28 - text: []const u8, │ │29 -) !void { │ │30 - var arena = std.heap.ArenaAllocator.init(allocator); │ │31 - defer arena.deinit(); │ │32 - for (try tokenize(arena.allocator(), text)) |word| { │ │33 - try addWord(DB, index, doc_key, word); │ │34 - } │ │35 -} │ │36 - │ │37 -// drop `doc_key`'s postings. the caller supplies the text again, so no forward │ │38 -// index is stored. │ │39 -pub fn remove( │ │40 - comptime DB: type, │ │41 - index: DB.SortedMap(.read_write), │ │42 - allocator: std.mem.Allocator, │ │43 - doc_key: []const u8, │ │44 - text: []const u8, │ │45 -) !void { │ │46 - var arena = std.heap.ArenaAllocator.init(allocator); │ │47 - defer arena.deinit(); │ │48 - for (try tokenize(arena.allocator(), text)) |word| { │ │49 - try removeWord(DB, index, doc_key, word); │ │50 - } │ │51 -} │ │21 │ │22 // move `doc_key` from `old_text`'s words to `new_text`'s. the words they share │ │23 // are left alone, so editing a long description costs what the edit changed │ ╰────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────╮ │55 -// rather than the whole text. │ │24 +// rather than the whole text. an empty side adds or drops the document.│ │25 pub fn replace( │ │26 comptime DB: type, │ │27 index: DB.SortedMap(.read_write), │ ╰────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────╮ │ 70 pub fn Query(comptime DB: type) type { │ │ 71 return struct { │ │ 72 // one per typed word; empty when nothing can match │ │104 - terms: []Term, │ │ 73 + terms: std.ArrayList(Term), │ │ 74 // the key the next result starts from, plus room for the one byte that│ │ 75 // steps past a returned key │ │ 76 pos: [max_doc_key_len + 1]u8 = undefined, │ ╰────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │148 try postings.append(aa, .{ .set = try DB.SortedSet(.read_only).init(pair.value_cursor) });│ │149 } │ │150 // a word nothing starts with rules out every document │ │182 - if (postings.items.len == 0) return .{ .terms = &.{} }; │ │151 + if (postings.items.len == 0) return .{ .terms = .empty }; │ │152 try terms.append(aa, .{ .postings = postings.items }); │ │153 } │ │185 - return .{ .terms = terms.items }; │ │154 + return .{ .terms = terms }; │ │155 } │ │156 │ │157 // narrow the results to the doc keys in `set`. it is one more term, │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────╮ │158 // with that set as its only posting. a query nothing can match stays │ │159 // empty, since requiring a set would turn it into every doc key in it. │ │160 pub fn require(self: *Self, aa: std.mem.Allocator, set: DB.SortedSet(.read_only)) !void {│ │192 - if (self.terms.len == 0) return; │ │193 - var terms: std.ArrayList(Term) = .empty; │ │194 - try terms.appendSlice(aa, self.terms); │ │161 + if (self.terms.items.len == 0) return; │ │162 const postings = try aa.alloc(Posting, 1); │ │163 postings[0] = .{ .set = set }; │ │197 - try terms.append(aa, .{ .postings = postings }); │ │198 - self.terms = terms.items; │ │164 + try self.terms.append(aa, .{ .postings = postings }); │ │165 } │ │166 │ │167 // start the next result at `key` rather than the first one. │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────╮ │172 } │ │173 │ │174 pub fn next(self: *Self) !?[]const u8 { │ │209 - if (self.terms.len == 0) return null; │ │175 + if (self.terms.items.len == 0) return null; │ │176 if (self.returned) { │ │177 // doc keys are at most max_doc_key_len, so appending a byte│ │178 // lands strictly after the one just returned │ ╰─────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │183 // intersect by seeking: a term that lands past the candidate makes │ │184 // its key the new candidate and the others seek up to it. │ │185 var i: usize = 0; │ │220 - while (i < self.terms.len) { │ │221 - const key = (try self.terms[i].seek(self.pos[0..self.pos_len])) orelse return null; │ │186 + while (i < self.terms.items.len) { │ │187 + const key = (try self.terms.items[i].seek(self.pos[0..self.pos_len])) orelse return null;│ │188 if (std.mem.order(u8, key, self.pos[0..self.pos_len]) == .gt) { │ │189 @memcpy(self.pos[0..key.len], key); │ │190 self.pos_len = key.len; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────╯