Merge lp:~jpakkane/unity-lens-applications/columbustestintegration into lp:unity-lens-applications

Proposed by Jussi Pakkanen
Status: Merged
Approved by: Paweł Stołowski
Approved revision: 335
Merged at revision: 326
Proposed branch: lp:~jpakkane/unity-lens-applications/columbustestintegration
Merge into: lp:unity-lens-applications
Diff against target: 451 lines (+210/-42)
6 files modified
configure.ac (+2/-1)
debian/control (+1/-0)
src/Makefile.am (+2/-2)
src/unity-package-search.cc (+162/-38)
tests/manual/fuzzy-search-test.txt (+42/-0)
tests/unit/Makefile.am (+1/-1)
To merge this branch: bzr merge lp:~jpakkane/unity-lens-applications/columbustestintegration
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Needs Fixing
Paweł Stołowski (community) Approve
Review via email: mp+150007@code.launchpad.net

Commit message

Add libcolumbus fuzzy matching.

Description of the change

Add libcolumbus fuzzy matching.

To post a comment you must log in.
Revision history for this message
Paweł Stołowski (stolowski) wrote :

325 + //cout << "Columbus matched " << results.size() << " documents." << endl;

Please remove.

357 + bool has_category = strstr(search_string, "category:") != NULL;
358 + const char *tmp = strstr(search_string, "AND");
359 + if(has_category || !tmp) {

Could you rename tmp? Shouldn't it actually say "tmp", not "!tmp" (we want complex queries to be handled by xapian search and only simple queries to be routed to columbus, right?). I don't quite understand what case we are trying to cover here and why - can you add a comment to this code?

Could you also add a manual tests to tests/manual/? Ideally it should fail completly if exectued on old app lens (without columbus); the test should cover cases with and without category filters.

Please check with didrocks if debian/changelog needs updating after chaning control.

review: Needs Fixing
Revision history for this message
Jussi Pakkanen (jpakkane) wrote :

The query string is of the type "xapian keyword stuff AND query text". If the query text is empty, the AND part is missing and strstr returns NULL. This is a Xapian keyword only search and thus we pass it to Xapian.

I renamed the variable so it is now clearer. Will contact didrocks about the changelog soon.

Revision history for this message
Jussi Pakkanen (jpakkane) wrote :

All issues have been fixed. The correct way to populate the changelog is to use bugs and link them. This has now been done.

However libcolumbus is currently in universe so let's not merge this until it gets to main.

Revision history for this message
Michael Terry (mterry) wrote :

libcolumbus's MIR is approved (bug 1122269). It's just waiting for something in main to depend on it before it gets promoted. If you want to be the first, go ahead and land this.

Revision history for this message
Paweł Stołowski (stolowski) wrote :

Looks good.

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
334. By Jussi Pakkanen

Bump required libcolumbus version.

335. By Jussi Pakkanen

Add libcolumbus flags to test subdirectory too.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'configure.ac'
2--- configure.ac 2013-02-15 10:11:07 +0000
3+++ configure.ac 2013-03-01 15:43:20 +0000
4@@ -14,7 +14,7 @@
5 #############################################
6 # Init the other things we depend on
7 #############################################
8-AM_MAINTAINER_MODE
9+AM_MAINTAINER_MODE([enable])
10 AM_PROG_VALAC([0.18.0])
11 AS_IF([test -z "$VALAC"], [AC_MSG_ERROR(["No valac compiler found."])])
12 AC_PROG_CC
13@@ -75,6 +75,7 @@
14 gee-1.0
15 dee-1.0 >= 0.5.16
16 zeitgeist-1.0 >= 0.3.8
17+ libcolumbus0 >= 0.4.0
18 unity >= 6.7.0
19 libgnome-menu-3.0 >= 3.6.0)
20
21
22=== modified file 'debian/control'
23--- debian/control 2012-11-08 04:49:06 +0000
24+++ debian/control 2013-03-01 15:43:20 +0000
25@@ -16,6 +16,7 @@
26 libxapian-dev,
27 libdb-dev,
28 libapt-pkg-dev,
29+ libcolumbus0-dev,
30 Standards-Version: 3.9.3
31 Homepage: https://launchpad.net/unity-lens-applications
32 # If you aren't a member of ~unity-team but need to upload packaging changes,
33
34=== modified file 'src/Makefile.am'
35--- src/Makefile.am 2013-02-20 15:31:37 +0000
36+++ src/Makefile.am 2013-03-01 15:43:20 +0000
37@@ -1,7 +1,7 @@
38 NULL =
39 BUILT_SOURCES =
40 CLEANFILES =
41-EXTRA_DIST =
42+EXTRA_DIST =
43
44 DATADIR = $(datadir)
45
46@@ -75,7 +75,7 @@
47 $(NULL)
48
49 unity-package-search.o : $(srcdir)/unity-package-search.cc $(srcdir)/unity-package-search.h
50- $(AM_V_GEN)$(CXX) $(COVERAGE_CFLAGS) -g $(unity_package_search_libs) -DGMENU_I_KNOW_THIS_IS_UNSTABLE `pkg-config --cflags --libs glib-2.0 libgnome-menu-3.0 unity gee-1.0` -c $(srcdir)/unity-package-search.cc
51+ $(AM_V_GEN)$(CXX) --std=c++11 $(COVERAGE_CFLAGS) -g $(unity_package_search_libs) -DGMENU_I_KNOW_THIS_IS_UNSTABLE `pkg-config --cflags --libs glib-2.0 libgnome-menu-3.0 unity gee-1.0 libcolumbus0` -c $(srcdir)/unity-package-search.cc
52
53 unity-ratings-db.o : $(srcdir)/unity-ratings-db.c $(srcdir)/unity-ratings-db.h
54 $(AM_V_CC)$(CC) -g $(COVERAGE_CFLAGS) $(unity_ratings_db_libs) `pkg-config --cflags --libs glib-2.0` -c $(srcdir)/unity-ratings-db.c
55
56=== modified file 'src/unity-package-search.cc'
57--- src/unity-package-search.cc 2012-11-05 19:18:41 +0000
58+++ src/unity-package-search.cc 2013-03-01 15:43:20 +0000
59@@ -25,14 +25,15 @@
60 * Xapian index of the menu contents.
61 */
62
63-using namespace std;
64-
65 #include <xapian.h>
66 #include <iostream>
67 #include <gmenu-tree.h>
68 #include <glib.h>
69 #include <unity.h>
70 #include <string.h>
71+#include <vector>
72+
73+using namespace std;
74
75 #define SOFTWARE_CENTER_INDEX "/var/cache/software-center/xapian"
76 #define QUERY_PARSER_EXACTSEARCH_FLAGS Xapian::QueryParser::FLAG_BOOLEAN|Xapian::QueryParser::FLAG_PHRASE|Xapian::QueryParser::FLAG_LOVEHATE
77@@ -55,14 +56,11 @@
78 #define XAPIAN_VALUE_SCREENSHOT_URLS 185
79 #define XAPIAN_VALUE_DESCRIPTION 188
80 #define XAPIAN_VALUE_VERSION_INFO 198
81+#define XAPIAN_VALUE_EXENAME 294
82 #define XAPIAN_VALUE_CURRENCY 201
83
84 #include "unity-package-search.h"
85-
86-extern "C"
87-{
88- extern gchar* unity_applications_lens_utils_preprocess_string (const gchar* input);
89-}
90+#include "columbus.hh"
91
92 struct _UnityPackageSearcher
93 {
94@@ -71,8 +69,58 @@
95 Xapian::Enquire *enquire;
96 Xapian::QueryParser *query_parser;
97 GRand *random;
98+ Columbus::Matcher *matcher;
99+ vector<string> col_mapping;
100+ bool db_merged;
101 };
102
103+static void buildMatcher(UnityPackageSearcher *searcher) {
104+ Columbus::Matcher *m = searcher->matcher;
105+ Xapian::Database *db = searcher->db;
106+ Columbus::Corpus c;
107+ Columbus::Word appnameField("appname");
108+ Columbus::Word summaryField("summary");
109+ Columbus::Word pkgnameField("pkgname");
110+ Columbus::Word exenameField("exename");
111+
112+ for(Xapian::PostingIterator post = db->postlist_begin(""); post != db->postlist_end(""); post++) {
113+ Xapian::Document xdoc = db->get_document(*post);
114+ DocumentID id;
115+ if(searcher->db_merged) {
116+ searcher->col_mapping.push_back(xdoc.get_value(XAPIAN_VALUE_APPNAME));
117+ id = searcher->col_mapping.size()-1;
118+ } else {
119+ id = xdoc.get_docid();
120+ }
121+ Columbus::Document cdoc(id);
122+ std::string val;
123+
124+ val = xdoc.get_value(XAPIAN_VALUE_APPNAME);
125+ if(!val.empty())
126+ cdoc.addText(appnameField, val.c_str());
127+ val = xdoc.get_value(XAPIAN_VALUE_SUMMARY);
128+ if(!val.empty())
129+ cdoc.addText(summaryField, val.c_str());
130+ val = xdoc.get_value(XAPIAN_VALUE_PKGNAME);
131+ if(!val.empty())
132+ cdoc.addText(pkgnameField, val.c_str());
133+ val = xdoc.get_value(XAPIAN_VALUE_EXENAME);
134+ if(!val.empty())
135+ cdoc.addText(exenameField, val.c_str());
136+ c.addDocument(cdoc);
137+ }
138+ m->index(c);
139+ m->getErrorValues().addStandardErrors();
140+ m->getErrorValues().setSubstringMode();
141+ m->getIndexWeights().setWeight(summaryField, 0.5);
142+}
143+
144+
145+extern "C"
146+{
147+ extern gchar* unity_applications_lens_utils_preprocess_string (const gchar* input);
148+}
149+
150 /* A Xapian::Sorter that respects the collation rules for the current locale */
151 class LocaleKeyMaker : public Xapian::KeyMaker
152 {
153@@ -264,6 +312,7 @@
154 dum2 = strstr (dum1, " "); // const
155 dum2 == NULL ? : *dum2 = '\0'; // const
156 dum2 = g_path_get_basename (dum1); // alloc
157+ doc.add_value(XAPIAN_VALUE_EXENAME, dum2);
158 indexer->index_text (dum2, 2);
159 g_free (dum1);
160 dum1 = g_strdelimit (dum2, "-", '_'); // in place switch, dum1 own mem
161@@ -299,7 +348,7 @@
162 UnityPackageSearcher *searcher;
163 Xapian::WritableDatabase *db;
164
165- searcher = g_new0 (UnityPackageSearcher, 1);
166+ searcher = new UnityPackageSearcher;
167 db = new Xapian::WritableDatabase ();
168 searcher->db = db;
169 searcher->db->add_database (Xapian::InMemory::open ());
170@@ -307,11 +356,15 @@
171 init_searcher (searcher);
172
173 /* Index the menu recursively */
174+ searcher->db_merged = false;
175 Xapian::TermGenerator *indexer = new Xapian::TermGenerator ();
176 index_menu_item (db, indexer, gmenu_tree_get_root_directory (menu));
177 delete indexer;
178 db->flush ();
179
180+ searcher->matcher = new Columbus::Matcher();
181+ buildMatcher(searcher);
182+
183 return searcher;
184 }
185
186@@ -323,7 +376,7 @@
187 UnityPackageSearcher *searcher;
188 gchar *agent = NULL;
189
190- searcher = g_new0 (UnityPackageSearcher, 1);
191+ searcher = new UnityPackageSearcher;
192
193 // Xapian initialization
194 try
195@@ -351,6 +404,9 @@
196 }
197
198 init_searcher (searcher);
199+ searcher->db_merged = true;
200+ searcher->matcher = new Columbus::Matcher();
201+ buildMatcher(searcher);
202
203 return searcher;
204 }
205@@ -364,8 +420,9 @@
206 delete searcher->sorter;
207 delete searcher->enquire;
208 delete searcher->query_parser;
209+ delete searcher->matcher;
210 g_rand_free (searcher->random);
211- g_free (searcher);
212+ delete searcher;
213 }
214
215 static UnityPackageInfo*
216@@ -425,20 +482,37 @@
217 g_slice_free (UnityPackageInfo, pkginfo);
218 }
219
220-UnityPackageSearchResult*
221-unity_package_searcher_search (UnityPackageSearcher *searcher,
222- const gchar *search_string,
223- guint max_hits,
224- UnityPackageSearchType search_type,
225- UnityPackageSort sort)
226+Xapian::Document get_doc_from_col_match(UnityPackageSearcher *searcher, DocumentID id) {
227+ if(searcher->db_merged) {
228+ string name = searcher->col_mapping[id];
229+ string query = "AA\"";
230+ query += name;
231+ query += "\"";
232+ Xapian::QueryParser p;
233+ Xapian::Query q;
234+ Xapian::Enquire e(*searcher->db);
235+ Xapian::MSet matches;
236+ p.set_database(*searcher->db);
237+ q = p.parse_query(query);
238+ e.set_query(q);
239+ matches = e.get_mset(0, 1);
240+ return matches.begin().get_document();
241+ } else {
242+ return searcher->db->get_document(id);
243+ }
244+}
245+
246+static UnityPackageSearchResult*
247+xapian_search (UnityPackageSearcher *searcher,
248+ const gchar *search_string,
249+ guint max_hits,
250+ UnityPackageSearchType search_type,
251+ UnityPackageSort sort)
252 {
253 UnityPackageSearchResult* result;
254-
255- g_return_val_if_fail (searcher != NULL, NULL);
256- g_return_val_if_fail (search_string != NULL, NULL);
257-
258 string _search_string (search_string);
259 Xapian::Query query;
260+
261 try
262 {
263 switch (search_type)
264@@ -455,12 +529,11 @@
265 break;
266 }
267 }
268- catch (Xapian::Error e)
269+ catch (Xapian::Error &e)
270 {
271 g_warning ("Error parsing query '%s': %s", search_string, e.get_msg().c_str());
272 return g_slice_new0 (UnityPackageSearchResult);
273 }
274- //cout << "Performing query `" << query.get_description() << "'" << endl;
275
276 switch (sort)
277 {
278@@ -488,24 +561,26 @@
279 // Retrieve the results, note that we build the result->results
280 // list in reverse order and then reverse it before we return it
281 result->num_hits = matches.get_matches_estimated ();
282+
283 for (Xapian::MSetIterator i = matches.begin(); i != matches.end(); ++i)
284- {
285- try
286- {
287- Xapian::Document doc = i.get_document();
288- UnityPackageInfo *pkginfo = _pkginfo_from_document (doc);
289- pkginfo->relevancy = i.get_percent ();
290- result->results = g_slist_prepend (result->results, pkginfo);
291- }
292- catch (Xapian::Error e)
293- {
294- g_warning ("Unable to read document from result set: %s", e.get_msg().c_str());
295- }
296- }
297+ {
298+ try
299+ {
300+ Xapian::Document doc = i.get_document();
301+ UnityPackageInfo *pkginfo = _pkginfo_from_document (doc);
302+ pkginfo->relevancy = i.get_percent();
303+ result->results = g_slist_prepend (result->results, pkginfo);
304+ }
305+ catch (Xapian::Error &e)
306+ {
307+ g_warning ("Unable to read document from result set: %s",
308+ e.get_msg().c_str());
309+ }
310+ }
311
312 result->results = g_slist_reverse (result->results);
313 }
314- catch(const Xapian::Error e)
315+ catch(const Xapian::Error &e)
316 {
317 g_warning ("Error running query '%s': %s", search_string, e.get_msg().c_str());
318 }
319@@ -513,6 +588,55 @@
320 return result;
321 }
322
323+static UnityPackageSearchResult*
324+libcolumbus_search(UnityPackageSearcher *searcher, const char *str) {
325+ Columbus::MatchResults results;
326+ UnityPackageSearchResult* result;
327+
328+ result = g_slice_new0 (UnityPackageSearchResult);
329+ searcher->matcher->match(str, results);
330+ for (size_t i = 0; i < results.size(); ++i)
331+ {
332+ try
333+ {
334+ Xapian::Document doc = get_doc_from_col_match(searcher, results.getDocumentID(i));
335+ UnityPackageInfo *pkginfo = _pkginfo_from_document (doc);
336+ pkginfo->relevancy = results.getRelevancy(i);
337+ result->results = g_slist_prepend (result->results, pkginfo);
338+ }
339+ catch (Xapian::Error &e)
340+ {
341+ g_warning ("Unable to read document from result set: %s",
342+ e.get_msg().c_str());
343+ }
344+ }
345+
346+ result->results = g_slist_reverse (result->results);
347+ return result;
348+}
349+
350+UnityPackageSearchResult*
351+unity_package_searcher_search (UnityPackageSearcher *searcher,
352+ const gchar *search_string,
353+ guint max_hits,
354+ UnityPackageSearchType search_type,
355+ UnityPackageSort sort)
356+{
357+
358+ g_return_val_if_fail (searcher != NULL, NULL);
359+ g_return_val_if_fail (search_string != NULL, NULL);
360+
361+ bool has_category = strstr(search_string, "category:") != NULL;
362+ const char *col_query_str = strstr(search_string, "AND");
363+ if(has_category || !col_query_str) {
364+ return xapian_search(searcher, search_string, max_hits, search_type, sort);
365+ } else {
366+ col_query_str += 3;
367+ return libcolumbus_search(searcher, col_query_str);
368+ }
369+}
370+
371+
372 /**
373 * Get applications matching given xapian filter query and additionally filter results out
374 * using AppFilterCallback, until n_apps matching apps are found.
375@@ -618,7 +742,7 @@
376 n_unique++;
377 }
378 }
379- catch (Xapian::Error e)
380+ catch (Xapian::Error &e)
381 {
382 g_warning ("Error getting random apps: %s", e.get_msg().c_str());
383 continue;
384@@ -653,7 +777,7 @@
385 }
386 }
387 }
388- catch (Xapian::Error e)
389+ catch (Xapian::Error &e)
390 {
391 g_debug ("Error getting random apps for query '%s': %s", filter_query, e.get_msg().c_str());
392 return g_slice_new0 (UnityPackageSearchResult);
393
394=== added file 'tests/manual/fuzzy-search-test.txt'
395--- tests/manual/fuzzy-search-test.txt 1970-01-01 00:00:00 +0000
396+++ tests/manual/fuzzy-search-test.txt 2013-03-01 15:43:20 +0000
397@@ -0,0 +1,42 @@
398+Fuzzy searching in the Dash
399+---------------------------
400+
401+Tests that fuzzy matching works in the Dash.
402+
403+Actions:
404+
405+Tap super.
406+Enter an application name that is slightly misspelled. Check that the Dash.
407+returns valid replies.
408+
409+Examples:
410+
411+ fimp -> Gimp
412+ fierfox -> Firefox
413+ sttings -> settings
414+
415+Fuzzy searching in the app lens
416+-------------------------------
417+
418+Tests that fuzzy matching works in the app lens.
419+
420+Actions:
421+
422+Tap super.
423+Select app lens.
424+Do the same searches as in the previous test. Check that they return the
425+same results.
426+
427+
428+Searching with filters
429+----------------------
430+
431+When filters are enabled, the search falls back to non-fuzzy mode.
432+
433+Actions:
434+
435+Tap super.
436+Select app lens.
437+Enable filters, activate "graphics".
438+Type "fimp" in search field, Gimp should not be show in the results.
439+Type "gimp" in search field, Gimp should be shown in the results.
440
441=== modified file 'tests/unit/Makefile.am'
442--- tests/unit/Makefile.am 2013-02-20 16:09:23 +0000
443+++ tests/unit/Makefile.am 2013-03-01 15:43:20 +0000
444@@ -106,7 +106,7 @@
445 nodist_test_software_center_details_SOURCES = $(test_software_center_details_VALASOURCES:.vala=.c)
446
447 unity-package-search.o : $(top_srcdir)/src/unity-package-search.cc $(top_srcdir)/src/unity-package-search.h
448- $(AM_V_GEN)$(CXX) -g $(COVERAGE_CFLAGS) $(UNITY_PACKAGE_SEARCH_LIBS) -DGMENU_I_KNOW_THIS_IS_UNSTABLE `pkg-config --cflags --libs glib-2.0 libgnome-menu-3.0 unity gee-1.0` -c $(top_srcdir)/src/unity-package-search.cc
449+ $(AM_V_GEN)$(CXX) --std=c++11 -g $(COVERAGE_CFLAGS) $(UNITY_PACKAGE_SEARCH_LIBS) -DGMENU_I_KNOW_THIS_IS_UNSTABLE `pkg-config --cflags --libs glib-2.0 libgnome-menu-3.0 unity gee-1.0 libcolumbus0` -c $(top_srcdir)/src/unity-package-search.cc
450 unity-ratings-db.o : $(top_srcdir)/src/unity-ratings-db.c $(top_srcdir)/src/unity-ratings-db.h
451 $(AM_V_CC)$(CC) -g $(COVERAGE_CFLAGS) $(unity_ratings_db_libs) `pkg-config --cflags --libs glib-2.0` -c $(top_srcdir)/src/unity-ratings-db.c
452

Subscribers

People subscribed via source and target branches