╭ changed ─────╮ │src/search.zig│ ╰──────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭────────────────────────────────────────────────────────────────────────────────────╮ │11 // still reaches it. │ │12 const max_word_len = 32; │ │13 // how many distinct words one document contributes. │ │14 -const max_words_per_doc = 64; │ │14 +const max_words_per_doc = 256; │ │15 // how much of a document is tokenized. │ │16 -pub const max_indexed_bytes = 4 * 1024; │ │16 +pub const max_indexed_bytes = 16 * 1024; │ │17 // how many indexed words one typed word expands to. │ │18 const max_expansions = 50; │ │19 // the largest doc key, so results are read into a buffer rather than allocated.│ ╰────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────╮ │93 // one indexed word's doc keys, positioned at the current one.│ │94 const Posting = struct { │ │95 set: DB.SortedSet(.read_only), │ │96 + iter: DB.Cursor(.read_only).Iter = undefined, │ │97 key: [max_doc_key_len]u8 = undefined, │ │98 key_len: usize = 0, │ │99 done: bool = false, │ ╰──────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │100 started: bool = false, │ │101 │ │101 - // move to the first key at or after `key`. seeking rather than │ │102 - // stepping keeps the cost proportional to the results. │ │102 + // move to the first key at or after `key`. one step reaches it in │ │103 + // a dense posting, and a seek skips the rest of a sparse one. │ │104 fn advance(self: *Posting, key: []const u8) !void { │ │105 if (self.done) return; │ │105 - if (self.started and std.mem.order(u8, self.key[0..self.key_len], key) != .lt) return;│ │106 - var iter = try self.set.iteratorFrom(key); │ │106 + if (self.started) { │ │107 + if (std.mem.order(u8, self.key[0..self.key_len], key) != .lt) return; │ │108 + try self.step(); │ │109 + if (self.done or std.mem.order(u8, self.key[0..self.key_len], key) != .lt) return;│ │110 + } │ │111 + self.iter = try self.set.iteratorFrom(key); │ │112 self.started = true; │ │108 - const cursor = (try iter.next()) orelse { │ │113 + try self.step(); │ │114 + } │ │115 + │ │116 + fn step(self: *Posting) !void { │ │117 + const cursor = (try self.iter.next()) orelse { │ │118 self.done = true; │ │119 return; │ │120 }; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────╮ │141 try terms.append(aa, .{ .postings = postings.items }); │ │142 } │ │143 return .{ .terms = terms.items }; │ │144 + } │ │145 + │ │146 + // narrow the results to the doc keys in `set`. it is one more term, │ │147 + // with that set as its only posting. a query nothing can match stays │ │148 + // empty, since requiring a set would turn it into every doc key in it. │ │149 + pub fn require(self: *Self, aa: std.mem.Allocator, set: DB.SortedSet(.read_only)) !void {│ │150 + if (self.terms.len == 0) return; │ │151 + var terms: std.ArrayList(Term) = .empty; │ │152 + try terms.appendSlice(aa, self.terms); │ │153 + const postings = try aa.alloc(Posting, 1); │ │154 + postings[0] = .{ .set = set }; │ │155 + try terms.append(aa, .{ .postings = postings }); │ │156 + self.terms = terms.items; │ │157 } │ │158 │ │159 // start the next result at `key` rather than the first one. │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯