Skip to content

Commit aeb6f39

Browse files
committed
Add suggestion search.
We are wrapping the suggestion part. With all the preparatory work, it is pretty simple.
1 parent 87093c1 commit aeb6f39

File tree

4 files changed

+202
-10
lines changed

4 files changed

+202
-10
lines changed

libzim/libwrapper.h

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <zim/writer/item.h>
3030
#include <zim/writer/contentProvider.h>
3131
#include <zim/search.h>
32+
#include <zim/suggestion.h>
3233

3334
struct _object;
3435
typedef _object PyObject;
@@ -201,6 +202,71 @@ class Searcher : public Wrapper<zim::Searcher>
201202
FORWARD(void, setVerbose)
202203
FORWARD(wrapper::Search, search)
203204
};
205+
206+
class SuggestionItem : public Wrapper<zim::SuggestionItem>
207+
{
208+
public:
209+
SuggestionItem() = default;
210+
SuggestionItem(const zim::SuggestionItem& o) : Wrapper(o) {};
211+
212+
FORWARD(std::string, getTitle)
213+
FORWARD(std::string, getPath)
214+
FORWARD(std::string, getSnippet)
215+
FORWARD(bool, hasSnippet)
216+
};
217+
218+
class SuggestionIterator : public Wrapper<zim::SuggestionIterator>
219+
{
220+
public:
221+
SuggestionIterator() = default;
222+
SuggestionIterator(const zim::SuggestionIterator& o) : Wrapper(o) {};
223+
zim::SuggestionIterator& operator*() const { return *mp_base; }
224+
225+
FORWARD(bool, operator==)
226+
bool operator!=(const wrapper::SuggestionIterator& it) const {
227+
return *mp_base != *it;
228+
}
229+
FORWARD(wrapper::SuggestionIterator, operator++)
230+
SuggestionItem getSuggestionItem() const {
231+
return mp_base->operator*();
232+
}
233+
// FORWARD(wrapper::SuggestionItem, operator*)
234+
FORWARD(wrapper::Entry, getEntry)
235+
};
236+
237+
class SuggestionResultSet : public Wrapper<zim::SuggestionResultSet>
238+
{
239+
public:
240+
SuggestionResultSet() = default;
241+
SuggestionResultSet(const zim::SuggestionResultSet& o) : Wrapper(o) {};
242+
243+
244+
FORWARD(wrapper::SuggestionIterator, begin)
245+
FORWARD(wrapper::SuggestionIterator, end)
246+
FORWARD(int, size)
247+
};
248+
249+
250+
class SuggestionSearch : public Wrapper<zim::SuggestionSearch>
251+
{
252+
public:
253+
SuggestionSearch() = default;
254+
SuggestionSearch(zim::SuggestionSearch&& s) : Wrapper(std::move(s)) {};
255+
256+
FORWARD(int, getEstimatedMatches)
257+
FORWARD(wrapper::SuggestionResultSet, getResults)
258+
};
259+
260+
class SuggestionSearcher : public Wrapper<zim::SuggestionSearcher>
261+
{
262+
public:
263+
SuggestionSearcher() = default;
264+
SuggestionSearcher(const wrapper::Archive& a) : Wrapper(zim::SuggestionSearcher(*a)) {};
265+
SuggestionSearcher(const zim::SuggestionSearcher& o) : Wrapper(o) {};
266+
267+
FORWARD(void, setVerbose)
268+
FORWARD(wrapper::SuggestionSearch, suggest)
269+
};
204270
} // namespace wrapper
205271
#undef FORWARD
206272

libzim/wrapper.pyx

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -955,14 +955,108 @@ search_public_objects = [
955955
search = create_module(search_module_name, search_module_doc, search_public_objects)
956956

957957

958+
###############################################################################
959+
#  Suggestion module #
960+
###############################################################################
961+
962+
suggestion_module_name = f"{__name__}.suggestion"
963+
964+
cdef class SuggestionResultSet:
965+
__module__ = suggestion_module_name
966+
cdef zim.SuggestionResultSet c_resultset
967+
968+
@staticmethod
969+
cdef from_resultset(zim.SuggestionResultSet _resultset):
970+
cdef SuggestionResultSet resultset = SuggestionResultSet()
971+
resultset.c_resultset = move(_resultset)
972+
return resultset
973+
974+
def __iter__(self):
975+
cdef zim.SuggestionIterator current = self.c_resultset.begin()
976+
cdef zim.SuggestionIterator end = self.c_resultset.end()
977+
while current != end:
978+
yield current.getSuggestionItem().getPath().decode('UTF-8')
979+
preincrement(current)
980+
981+
cdef class SuggestionSearch:
982+
__module__ = suggestion_module_name
983+
cdef zim.SuggestionSearch c_search
984+
985+
# Factory functions - Currently Cython can't use classmethods
986+
@staticmethod
987+
cdef from_search(zim.SuggestionSearch _search):
988+
""" Creates a python ReadArticle from a C++ Article (zim::) -> ReadArticle
989+
990+
Parameters
991+
----------
992+
_item : Item
993+
A C++ Item
994+
Returns
995+
------
996+
Item
997+
Casted item """
998+
cdef SuggestionSearch search = SuggestionSearch()
999+
search.c_search = move(_search)
1000+
return search
1001+
1002+
def getEstimatedMatches(self):
1003+
return self.c_search.getEstimatedMatches()
1004+
1005+
def getResults(self, start, count):
1006+
return SuggestionResultSet.from_resultset(move(self.c_search.getResults(start, count)))
1007+
1008+
1009+
cdef class SuggestionSearcher:
1010+
""" Zim Archive SuggestionSearcher
1011+
1012+
Attributes
1013+
----------
1014+
*c_archive : Searcher
1015+
a pointer to a C++ Searcher object
1016+
"""
1017+
__module__ = suggestion_module_name
1018+
1019+
cdef zim.SuggestionSearcher c_searcher
1020+
1021+
def __cinit__(self, object archive: Archive):
1022+
""" Constructs an Archive from full zim file path
1023+
1024+
Parameters
1025+
----------
1026+
filename : pathlib.Path
1027+
Full path to a zim file """
1028+
1029+
self.c_searcher = move(zim.SuggestionSearcher(archive.c_archive))
1030+
1031+
def suggest(self, query: str):
1032+
return SuggestionSearch.from_search(move(self.c_searcher.suggest(query.encode('utf8'))))
1033+
1034+
suggestion_module_doc = """ libzim suggestion module
1035+
1036+
Usage:
1037+
1038+
archive = Archive(fpath)
1039+
suggestion_searcher = SuggestionSearcher(archive)
1040+
suggestion = suggestion searcher.suggest("foo")
1041+
resultSet = suggestion.getResult(10, 10) # get result from 10 to 20 (10 results)
1042+
for path in resultSet:
1043+
print(path)"""
1044+
suggestion_public_objects = [
1045+
SuggestionSearcher
1046+
]
1047+
suggestion = create_module(suggestion_module_name, suggestion_module_doc, suggestion_public_objects)
1048+
1049+
1050+
9581051
class ModuleLoader(importlib.abc.Loader):
9591052
# Create our module. Easy, just return the created module
9601053
@staticmethod
9611054
def create_module(spec):
9621055
return {
9631056
'libzim.writer': writer,
9641057
'libzim.reader': reader,
965-
'libzim.search': search
1058+
'libzim.search': search,
1059+
'libzim.suggestion': suggestion
9661060
}.get(spec.name, None)
9671061

9681062
@staticmethod
@@ -981,5 +1075,5 @@ class ModuleFinder(importlib.abc.MetaPathFinder):
9811075
# register finder for our submodules
9821076
sys.meta_path.insert(0, ModuleFinder())
9831077

984-
__all__ = ["writer", "reader", "search"]
1078+
__all__ = ["writer", "reader", "search", "suggestion"]
9851079

libzim/zim.pxd

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,3 +176,32 @@ cdef extern from "libwrapper.h" namespace "wrapper":
176176
SearchIterator begin()
177177
SearchIterator end()
178178
int size()
179+
180+
cdef cppclass SuggestionItem:
181+
string getPath()
182+
string getTitle()
183+
string getSnippet()
184+
bool hasSnippet()
185+
186+
cdef cppclass SuggestionIterator:
187+
SuggestionIterator()
188+
SuggestionIterator operator++()
189+
bint operator==(SuggestionIterator)
190+
bint operator!=(SuggestionIterator)
191+
SuggestionItem getSuggestionItem()
192+
Entry getEntry()
193+
194+
cdef cppclass SuggestionSearcher:
195+
SuggestionSearcher()
196+
SuggestionSearcher(const Archive& archive) except +
197+
setVerbose(bool verbose)
198+
SuggestionSearch suggest(string query) except +
199+
200+
cdef cppclass SuggestionSearch:
201+
int getEstimatedMatches() except +
202+
SuggestionResultSet getResults(int start, int count) except +
203+
204+
cdef cppclass SuggestionResultSet:
205+
SuggestionIterator begin()
206+
SuggestionIterator end()
207+
int size()

tests/test_libzim_reader.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import libzim.writer
1212
from libzim.reader import Archive
1313
from libzim.search import Searcher, Query
14+
from libzim.suggestion import SuggestionSearcher
1415

1516

1617
# expected data for tests ZIMs (see `all_zims`)
@@ -122,9 +123,12 @@
122123
"entry_count": 60,
123124
"all_entry_count": 75,
124125
"article_count": 0,
125-
"suggestion_string": "favicon",
126+
"suggestion_string": "Free",
126127
"suggestion_count": 1,
127-
"suggestion_result": ["favicon.png"],
128+
"suggestion_result": [
129+
"FreedomBox for Communities_Offline Wikipedia "
130+
+ "- Wikibooks, open books for an open world.html"
131+
],
128132
"search_string": "main",
129133
"search_count": 2,
130134
"search_result": [
@@ -411,12 +415,11 @@ def test_reader_suggest_search(
411415
assert search.getEstimatedMatches() == search_count
412416
assert list(search.getResults(0, search_count)) == search_result
413417

414-
#TODO: restore suggestion search
415-
# assert (
416-
# zim.get_estimated_suggestions_results_count(suggestion_string)
417-
# == suggestion_count
418-
# )
419-
# assert list(zim.suggest(suggestion_string)) == suggestion_result
418+
if suggestion_string is not None:
419+
suggestion_searcher = SuggestionSearcher(zim)
420+
suggestion = suggestion_searcher.suggest(suggestion_string)
421+
assert suggestion.getEstimatedMatches() == suggestion_count
422+
assert list(suggestion.getResults(0, suggestion_count)) == suggestion_result
420423

421424

422425
@pytest.mark.parametrize(

0 commit comments

Comments
 (0)