+97 -92 +/-11 browse
1 | diff --git a/doc/note-cat.1.scd b/doc/note-cat.1.scd |
2 | index 7c16ed2..301e083 100644 |
3 | --- a/doc/note-cat.1.scd |
4 | +++ b/doc/note-cat.1.scd |
5 | @@ -26,9 +26,6 @@ json for consumption by other tools. |
6 | *-encoding* |
7 | specify the output encoding |
8 | |
9 | - *-fulltext* |
10 | - perform full text search instead of key comparision |
11 | - |
12 | # EXAMPLES |
13 | ``` |
14 | # print the parsed content of the fuubar note |
15 | diff --git a/doc/note-delete.1.scd b/doc/note-delete.1.scd |
16 | index d526886..af09121 100644 |
17 | --- a/doc/note-delete.1.scd |
18 | +++ b/doc/note-delete.1.scd |
19 | @@ -19,11 +19,6 @@ Delete one or more notes that match the filter criteria. |
20 | *FILTER* |
21 | tag or text based search terms |
22 | |
23 | - *optional arguments* |
24 | - |
25 | - *-fulltext* |
26 | - perform full text search instead of key comparision |
27 | - |
28 | # EXAMPLES |
29 | ``` |
30 | # delete a note called fuubar |
31 | diff --git a/doc/note-edit.1.scd b/doc/note-edit.1.scd |
32 | index f86b349..72792de 100644 |
33 | --- a/doc/note-edit.1.scd |
34 | +++ b/doc/note-edit.1.scd |
35 | @@ -19,11 +19,6 @@ Select a note that matches the filter criteria and open it in your text editor. |
36 | *FILTER* |
37 | tag or text based search terms |
38 | |
39 | - *optional arguments* |
40 | - |
41 | - *-fulltext* |
42 | - perform full text search instead of key comparision |
43 | - |
44 | # EXAMPLES |
45 | ``` |
46 | # edit a note called fuubar |
47 | diff --git a/doc/note-ls.1.scd b/doc/note-ls.1.scd |
48 | index 208bea1..9682140 100644 |
49 | --- a/doc/note-ls.1.scd |
50 | +++ b/doc/note-ls.1.scd |
51 | @@ -25,9 +25,6 @@ is provided then all notes will be listed. |
52 | *-columns* |
53 | columns to show in the note list |
54 | |
55 | - *-fulltext* |
56 | - perform full text search instead of key comparision |
57 | - |
58 | *-style* |
59 | list style [fixed | wide | simple] |
60 | |
61 | diff --git a/lib/cmd.ml b/lib/cmd.ml |
62 | index b950400..8984065 100644 |
63 | --- a/lib/cmd.ml |
64 | +++ b/lib/cmd.ml |
65 | @@ -72,9 +72,6 @@ json for consumption by other tools. |
66 | |}) |
67 | [%map_open |
68 | let filter_args = anon (sequence ("filter" %: filter_arg)) |
69 | - and fulltext = |
70 | - flag "fulltext" no_arg |
71 | - ~doc:"perform a fulltext search instead of just key comparison" |
72 | and encoding = |
73 | flag "encoding" |
74 | (optional_with_default cfg.encoding encoding_arg) |
75 | @@ -82,10 +79,7 @@ json for consumption by other tools. |
76 | in |
77 | fun () -> |
78 | let open Note.Search in |
79 | - let filter_kind = if fulltext then Some Fulltext else None in |
80 | - let notes = |
81 | - find_many ?strategy:filter_kind ~args:filter_args get_notes |
82 | - in |
83 | + let notes = find_many ~args:filter_args get_notes in |
84 | List.iter |
85 | ~f:(fun note -> |
86 | print_endline (Note.Encoding.to_string ~style:encoding note)) |
87 | @@ -148,18 +142,11 @@ let delete_note = |
88 | Delete the first note that matches the filter criteria. |
89 | |}) |
90 | [%map_open |
91 | - let filter_args = anon (sequence ("filter" %: filter_arg)) |
92 | - and fulltext = |
93 | - flag "fulltext" no_arg |
94 | - ~doc:"perform a fulltext search instead of just key comparison" |
95 | - in |
96 | + let filter_args = anon (sequence ("filter" %: filter_arg)) in |
97 | fun () -> |
98 | let open Note.Search in |
99 | - let filter_kind = if fulltext then Fulltext else Keys in |
100 | let notes = get_notes in |
101 | - let note = |
102 | - Note.Search.find_one ~strategy:filter_kind ~args:filter_args notes |
103 | - in |
104 | + let note = find_one ~args:filter_args notes in |
105 | match note with |
106 | | Some note -> |
107 | Io.delete ~callback:cfg.on_modification ~title:(Note.get_title note) |
108 | @@ -174,15 +161,10 @@ let edit_note = |
109 | Select a note that matches the filter criteria and open it in your text editor. |
110 | |}) |
111 | [%map_open |
112 | - let filter_args = anon (sequence ("filter" %: filter_arg)) |
113 | - and fulltext = |
114 | - flag "fulltext" no_arg |
115 | - ~doc:"perform a fulltext search instead of just key comparison" |
116 | - in |
117 | + let filter_args = anon (sequence ("filter" %: filter_arg)) in |
118 | fun () -> |
119 | let open Note.Search in |
120 | - let filter_kind = if fulltext then Fulltext else Keys in |
121 | - let note = find_one ~strategy:filter_kind ~args:filter_args get_notes in |
122 | + let note = find_one ~args:filter_args get_notes in |
123 | match note with |
124 | | Some note -> |
125 | Io.edit ~callback:cfg.on_modification ~editor:cfg.editor |
126 | @@ -199,9 +181,6 @@ is provided then all notes will be listed. |
127 | |}) |
128 | [%map_open |
129 | let filter_args = anon (sequence ("filter" %: filter_arg)) |
130 | - and fulltext = |
131 | - flag "fulltext" no_arg |
132 | - ~doc:"perform a fulltext search instead of just key comparison" |
133 | and style = |
134 | flag "style" |
135 | (optional_with_default cfg.list_style list_style_arg) |
136 | @@ -213,15 +192,10 @@ is provided then all notes will be listed. |
137 | in |
138 | fun () -> |
139 | let open Note.Search in |
140 | - let filter_kind = if fulltext then Some Fulltext else None in |
141 | - let notes = |
142 | - Note.Search.find_many ?strategy:filter_kind ~args:filter_args |
143 | - get_notes |
144 | - in |
145 | + let notes = find_many ~args:filter_args get_notes in |
146 | let styles = cfg.styles in |
147 | let cells = Note.to_cells ~columns ~styles notes in |
148 | - Display.to_stdout ~style cells; |
149 | - ] |
150 | + Display.to_stdout ~style cells] |
151 | |
152 | let sync = |
153 | Command.basic ~summary:"sync notes to a remote server" |
154 | diff --git a/lib/dune b/lib/dune |
155 | index 32b55d4..05d525c 100644 |
156 | --- a/lib/dune |
157 | +++ b/lib/dune |
158 | @@ -2,4 +2,4 @@ |
159 | (name note_lib) |
160 | (preprocess |
161 | (pps ppx_jane)) |
162 | - (libraries ANSITerminal base core dune-build-info ezjsonm omd stdio yaml)) |
163 | + (libraries ANSITerminal base core dune-build-info ezjsonm omd stdio re yaml)) |
164 | diff --git a/lib/note.ml b/lib/note.ml |
165 | index 229760d..c33f8bc 100644 |
166 | --- a/lib/note.ml |
167 | +++ b/lib/note.ml |
168 | @@ -166,41 +166,59 @@ module Encoding = struct |
169 | end |
170 | |
171 | module Search = struct |
172 | + open Re.Str |
173 | |
174 | - type strategy = Keys | Fulltext |
175 | + let dump_results results = |
176 | + List.iter |
177 | + ~f:(fun result -> |
178 | + print_endline (sprintf "%s - %d" (get_title (snd result)) (fst result))) |
179 | + results |
180 | |
181 | - let title key note = String.equal key (get_title note) |
182 | + let title expr note = |
183 | + let title_string = get_title note in |
184 | + string_match expr title_string 0 |
185 | |
186 | - let tags key note = |
187 | + let tags expr note = |
188 | let tags = get_tags note in |
189 | - List.count ~f:(fun tag -> String.equal key tag) tags > 0 |
190 | + List.count ~f:(fun tag -> string_match expr tag 0) tags > 0 |
191 | |
192 | - let of_strings strategy args = |
193 | - match strategy with |
194 | - | Keys -> |
195 | - List.map |
196 | - ~f:(fun arg -> |
197 | - let has_title = title arg in |
198 | - let has_tag = tags arg in |
199 | - fun note -> has_title note || has_tag note) |
200 | - args |
201 | - | Fulltext -> failwith "not implemented" |
202 | + let content expr note = |
203 | + let words = Util.to_words note.markdown in |
204 | + List.count ~f:(fun word -> string_match expr word 0) words > 0 |
205 | |
206 | - let find_one ?(strategy = Keys) ~args notes = |
207 | - let filters = of_strings strategy args in |
208 | - List.find |
209 | - ~f:(fun note -> |
210 | - List.count ~f:(fun filter -> filter note) filters > 0 |
211 | - || List.length filters = 0) |
212 | - notes |
213 | + let match_and_rank ~args notes = |
214 | + let expressions = List.map ~f:regexp args in |
215 | + let matches = |
216 | + List.fold ~init:[] |
217 | + ~f:(fun accm note -> |
218 | + let has_title = |
219 | + List.count ~f:(fun expr -> title expr note) expressions > 0 |
220 | + in |
221 | + let has_tag = |
222 | + List.count ~f:(fun expr -> tags expr note) expressions > 0 |
223 | + in |
224 | + let has_content = |
225 | + List.count ~f:(fun expr -> content expr note) expressions > 0 |
226 | + in |
227 | + match (has_title, has_tag, has_content) with |
228 | + | true, _, _ -> List.append accm [ (20, note) ] |
229 | + | _, true, _ -> List.append accm [ (10, note) ] |
230 | + | _, _, true -> List.append accm [ (5, note) ] |
231 | + | false, false, false -> accm) |
232 | + notes |
233 | + in |
234 | + List.rev (List.sort ~compare:(fun n1 n2 -> fst n1 - fst n2) matches) |
235 | |
236 | - let find_many ?(strategy = Keys) ~args notes = |
237 | - let filters = of_strings strategy args in |
238 | - List.filter |
239 | - ~f:(fun note -> |
240 | - List.count ~f:(fun filter -> filter note) filters > 0 |
241 | - || List.length filters = 0) |
242 | - notes |
243 | + let find_one ~args notes = |
244 | + let results = match_and_rank ~args notes in |
245 | + let results = List.map ~f:snd results in |
246 | + if List.length results = 0 then None else Some (List.hd_exn results) |
247 | + |
248 | + let find_many ~args notes = |
249 | + if List.length args = 0 then notes |
250 | + else |
251 | + let results = match_and_rank ~args notes in |
252 | + List.map ~f:snd results |
253 | end |
254 | |
255 | open ANSITerminal |
256 | diff --git a/note.opam b/note.opam |
257 | index a3056a0..38f9716 100644 |
258 | --- a/note.opam |
259 | +++ b/note.opam |
260 | @@ -14,6 +14,7 @@ depends: [ |
261 | "dune" {>= "2.7" & >= "2.7"} |
262 | "ezjsonm" {>= "1.2.0"} |
263 | "omd" {>= "1.3.1"} |
264 | + "re" {>= "v1.9.0"} |
265 | "stdio" {>= "v0.14.0"} |
266 | "yaml" {>= "2.1.0"} |
267 | "odoc" {with-doc} |
268 | diff --git a/test/dune b/test/dune |
269 | index c8191e5..5ed362e 100644 |
270 | --- a/test/dune |
271 | +++ b/test/dune |
272 | @@ -1,4 +1,4 @@ |
273 | (tests |
274 | - (names e2e filter) |
275 | + (names e2e search) |
276 | (libraries note_lib) |
277 | ) |
278 | diff --git a/test/filter.ml b/test/filter.ml |
279 | deleted file mode 100644 |
280 | index cd850ad..0000000 |
281 | --- a/test/filter.ml |
282 | +++ /dev/null |
283 | @@ -1,13 +0,0 @@ |
284 | - open Note_lib |
285 | - |
286 | - let test_filter_by_keys = |
287 | - let state_dir = Core.Filename.temp_dir "note-test" "" in |
288 | - let slug = Slug.next state_dir in |
289 | - let note = |
290 | - Note.build ~tags:[ "fuu"; "bar" ] ~content:"" ~title:"A Very Important Note" slug |
291 | - in |
292 | - let result = Note.Filter.find_many ~strategy: Keys ~args: ["fuu" ; "bar"; "baz"] [note] in |
293 | - assert (List.length result = 1) |
294 | - |
295 | - let () = |
296 | - test_filter_by_keys; |
297 | diff --git a/test/search.ml b/test/search.ml |
298 | new file mode 100644 |
299 | index 0000000..5a47765 |
300 | --- /dev/null |
301 | +++ b/test/search.ml |
302 | @@ -0,0 +1,41 @@ |
303 | + open Note_lib |
304 | + |
305 | + let make_notes = |
306 | + let state_dir = Core.Filename.temp_dir "note-test" "" in |
307 | + let note_1 = |
308 | + Note.build ~tags:[ "fuu"; "bar" ] ~content:"" ~title:"A Very Important Note" |
309 | + (Slug.next state_dir) |
310 | + in |
311 | + let note_2 = |
312 | + Note.build ~tags:[ "fuu"; "bar"; "baz" ] ~content:"" |
313 | + ~title:"Another Very Important Note" (Slug.next state_dir) |
314 | + in |
315 | + let note_3 = |
316 | + Note.build ~tags:[ "fuu"; "bar"; "baz" ] ~content:"" |
317 | + ~title:"fuu" (Slug.next state_dir) |
318 | + in |
319 | + [note_1 ; note_2 ; note_3] |
320 | + |
321 | + |
322 | + let test_filter_by_keys = |
323 | + let notes = make_notes in |
324 | + let result = |
325 | + Note.Search.find_many ~args:[ "fuu"; "bar"; "baz" ] |
326 | + notes |
327 | + in |
328 | + assert (List.length result = 3) |
329 | + |
330 | + let test_filter_by_title_find_one = |
331 | + let notes = make_notes in |
332 | + let result = |
333 | + Note.Search.find_one ~args:[ "fuu" ] |
334 | + notes |
335 | + in |
336 | + assert (Option.is_some result) ; |
337 | + let note = Option.get result in |
338 | + (* title should take priority *) |
339 | + assert ((Note.get_title note) = "fuu") |
340 | + |
341 | + let () = |
342 | + test_filter_by_keys; |
343 | + test_filter_by_title_find_one; |