Merge lp:~kalikiana/u1db-qt/indexRoles into lp:u1db-qt
- indexRoles
- Merge into trunk
Status: | Needs review | ||||
---|---|---|---|---|---|
Proposed branch: | lp:~kalikiana/u1db-qt/indexRoles | ||||
Merge into: | lp:u1db-qt | ||||
Diff against target: |
485 lines (+349/-8) 6 files modified
debian/control (+2/-0) examples/bookmarks/bookmarks.qml (+6/-3) examples/movies/movies.qml (+196/-0) src/query.cpp (+12/-2) tests/test-database.cpp (+15/-0) tests/tst_query.qml (+118/-3) |
||||
To merge this branch: | bzr merge lp:~kalikiana/u1db-qt/indexRoles | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot | continuous-integration | Approve | |
Benjamin Zeller | Pending | ||
Review via email: mp+211771@code.launchpad.net |
Commit message
Expose index fields as role names
Description of the change
This is aimed to work with lp:~kalikiana/ubuntu-ui-toolkit/sortFilterModel.
TODO: Before merging we should have a unit test for it.
PS Jenkins bot (ps-jenkins) wrote : | # |
Andrew Hayzen (ahayzen) wrote : | # |
FYI the branch which I was having issues sorting can be found here [0].
0 - https:/
- 118. By Cris Dywan
-
Add sort and filter unit test cases and movies example
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:118
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 119. By Cris Dywan
-
Build and examples should depend on UI toolkit plugin
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:119
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 120. By Cris Dywan
-
Merge lp:u1db-qt
- 121. By Cris Dywan
-
Add C++ unit test checking role names of Query
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:121
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 122. By Cris Dywan
-
Work-around for sort.property not correctly re-sorting
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:122
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Unmerged revisions
- 122. By Cris Dywan
-
Work-around for sort.property not correctly re-sorting
- 121. By Cris Dywan
-
Add C++ unit test checking role names of Query
- 120. By Cris Dywan
-
Merge lp:u1db-qt
- 119. By Cris Dywan
-
Build and examples should depend on UI toolkit plugin
- 118. By Cris Dywan
-
Add sort and filter unit test cases and movies example
- 117. By Cris Dywan
-
Expose index fields as role names
Preview Diff
1 | === modified file 'debian/control' |
2 | --- debian/control 2014-01-22 13:14:22 +0000 |
3 | +++ debian/control 2014-11-12 17:08:18 +0000 |
4 | @@ -14,6 +14,7 @@ |
5 | qtdeclarative5-qtquick2-plugin, |
6 | qtdeclarative5-test-plugin, |
7 | qttools5-dev-tools, |
8 | + qtdeclarative5-ubuntu-ui-toolkit-plugin, |
9 | ubuntu-ui-toolkit-doc, |
10 | xvfb, |
11 | libgl1-mesa-dri, |
12 | @@ -65,6 +66,7 @@ |
13 | Section: doc |
14 | Architecture: all |
15 | Depends: libu1db-qt5-3 (>= ${source:Version}), |
16 | + qtdeclarative5-ubuntu-ui-toolkit-plugin, |
17 | ${misc:Depends}, |
18 | Description: Qt5 binding and QtQuick2 plugin for U1DB - examples |
19 | Simple Qt5 binding and QtQuick2 plugin for U1DB (https://launchpad.net/u1db). |
20 | |
21 | === modified file 'examples/bookmarks/bookmarks.qml' |
22 | --- examples/bookmarks/bookmarks.qml 2014-02-17 17:40:42 +0000 |
23 | +++ examples/bookmarks/bookmarks.qml 2014-11-12 17:08:18 +0000 |
24 | @@ -123,8 +123,8 @@ |
25 | property string detailsDocId: "" |
26 | property string detailsContents: "" |
27 | delegate: ListItem.Subtitled { |
28 | - text: contents.title || '[title:%1]'.arg(docId) |
29 | - subText: contents.uri || '[uri:%1]'.arg(docId) |
30 | + text: title |
31 | + subText: uri |
32 | // iconSource: contents.uri + "/favicon.ico" |
33 | fallbackIconName: "favorite-unselected,text-html" |
34 | iconFrame: false |
35 | @@ -147,7 +147,7 @@ |
36 | ListElement { label: 'Ubuntu'; expression: "[ 'uri' ]"; query: "[ 'http://www.ubuntu*' ]" } |
37 | ListElement { label: 'Search'; expression: "[ 'meta.title' ]"; query: "[ 'Search*' ]" } |
38 | ListElement { label: 'Engine'; expression: "[ 'meta.tags' ]"; query: "[ 'engine' ]" } |
39 | - ListElement { label: 'All'; expression: "[ 'meta.visits', 'meta.title' ]"; query: "[ '*', '*' ]" } |
40 | + ListElement { label: 'All'; expression: "[ 'meta.visits', 'meta.title', 'uri' ]"; query: "[ '*', '*', '*' ]" } |
41 | } |
42 | delegate: OptionSelectorDelegate { |
43 | text: i18n.tr(label) |
44 | @@ -156,8 +156,11 @@ |
45 | onSelectedIndexChanged: { |
46 | var d = model.get(selectedIndex) |
47 | text = '%1 - %2'.arg(d.expression).arg(d.query) |
48 | + bookmarksList.model = null |
49 | allBookmarks.index.expression = eval(d.expression) |
50 | allBookmarks.query = eval(d.query) |
51 | + // Force update of delegates and role names |
52 | + bookmarksList.model = allBookmarks |
53 | } |
54 | } |
55 | } |
56 | |
57 | === added directory 'examples/movies' |
58 | === added file 'examples/movies/movies.qml' |
59 | --- examples/movies/movies.qml 1970-01-01 00:00:00 +0000 |
60 | +++ examples/movies/movies.qml 2014-11-12 17:08:18 +0000 |
61 | @@ -0,0 +1,196 @@ |
62 | +/* |
63 | + * Copyright (C) 2014 Canonical, Ltd. |
64 | + * |
65 | + * Authors: |
66 | + * Christian Dywan <christian.dywan@canonical.com> |
67 | + * |
68 | + * This program is free software; you can redistribute it and/or modify |
69 | + * it under the terms of the GNU Lesser General Public License as published by |
70 | + * the Free Software Foundation; version 3. |
71 | + * |
72 | + * This program is distributed in the hope that it will be useful, |
73 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
74 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
75 | + * GNU Lesser General Public License for more details. |
76 | + * |
77 | + * You should have received a copy of the GNU Lesser General Public License |
78 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
79 | + */ |
80 | + |
81 | +import QtQuick 2.0 |
82 | +import U1db 1.0 as U1db |
83 | +import Ubuntu.Components 1.1 |
84 | +import Ubuntu.Components.ListItems 0.1 as ListItem |
85 | +import Ubuntu.Components.Popups 0.1 |
86 | + |
87 | +MainView { |
88 | + id: root |
89 | + applicationName: "com.ubuntu.developer.foobar.movies" |
90 | + |
91 | + width: units.gu(45) |
92 | + height: units.gu(80) |
93 | + |
94 | + /* |
95 | + Movie database |
96 | + */ |
97 | + |
98 | + U1db.Database { |
99 | + id: db |
100 | + // path: "movies.db" |
101 | + } |
102 | + |
103 | + // Defaults taken from http://archive.blender.org/features-gallery/movies/index.html |
104 | + |
105 | + U1db.Document { |
106 | + database: db |
107 | + docId: 'yBigBuckBunny' |
108 | + create: true |
109 | + defaults: { |
110 | + 'title': 'Big Buck Bunny (Short film)', |
111 | + producer: 'Blender Foundation', |
112 | + year: 2008, duration: '10min', website: 'https://www.bigbugckbunny.org' |
113 | + } |
114 | + } |
115 | + |
116 | + U1db.Document { |
117 | + database: db |
118 | + docId: 'zElephantsDream' |
119 | + create: true |
120 | + defaults: { |
121 | + 'title': 'Elephants Dream (Short film)', |
122 | + producer: 'Blender Foundation', |
123 | + year: 2006, duration: '10min', website: 'https://www.elephantsdream.org' |
124 | + } |
125 | + } |
126 | + |
127 | + U1db.Document { |
128 | + database: db |
129 | + docId: 'cProjectLondon' |
130 | + create: true |
131 | + defaults: { |
132 | + 'title': 'Project London Trailer', |
133 | + producer: 'Independent', |
134 | + year: 2009, duration: '22sec', website: 'https://www.projectlondonmovie.com' |
135 | + } |
136 | + } |
137 | + |
138 | + U1db.Document { |
139 | + database: db |
140 | + docId: 'aSoftBoy' |
141 | + create: true |
142 | + defaults: { |
143 | + 'title': 'Soft Boy (Animation)', |
144 | + producer: 'Andy Goralczyk', |
145 | + year: 2005, duration: '17sec' |
146 | + } |
147 | + } |
148 | + |
149 | + U1db.Document { |
150 | + database: db |
151 | + docId: 'bEsign' |
152 | + create: true |
153 | + defaults: { |
154 | + 'title': 'Esign (Animation)', |
155 | + producer: 'Chris Larkee', |
156 | + year: 2005, duration: '4min' |
157 | + } |
158 | + } |
159 | + |
160 | + U1db.Query { |
161 | + id: allMovies |
162 | + index: blacklist |
163 | + query: [ { year: '*', producer: '*', duration: '*', title: '*' } ] |
164 | + } |
165 | + |
166 | + U1db.Index { |
167 | + id: blacklist |
168 | + database: db |
169 | + expression: [ 'year', 'duration', 'title', 'producer' ] |
170 | + } |
171 | + |
172 | + SortFilterModel { |
173 | + id: sortedMovies |
174 | + model: allMovies |
175 | + filter.pattern: RegExp(searchProducer.text) |
176 | + filter.property: 'producer' |
177 | + sort.property: sortBy.role |
178 | + sort.order: Qt.DescendingOrder |
179 | + } |
180 | + |
181 | + /* |
182 | + UI: list view, filters |
183 | + */ |
184 | + |
185 | + Page { |
186 | + id: page |
187 | + title: i18n.tr('Movies') |
188 | + |
189 | + Column { |
190 | + id: container |
191 | + anchors.margins: units.gu(1) |
192 | + anchors.fill: parent |
193 | + |
194 | + ListView { |
195 | + id: movieList |
196 | + width: parent.width |
197 | + height: parent.height - pillar.height |
198 | + model: sortedMovies |
199 | + delegate: ListItem.Subtitled { |
200 | + text: '<b>%1</b> %2'.arg(title).arg(year) |
201 | + subText: '%1 <b>%2</b>'.arg(duration).arg(producer) |
202 | + fallbackIconName: "favorite-unselected,text-html" |
203 | + iconFrame: false |
204 | + } |
205 | + } |
206 | + |
207 | + Column { |
208 | + id: pillar |
209 | + spacing: units.gu(1) |
210 | + width: parent.width |
211 | + |
212 | + Label { |
213 | + text: sortBy.role |
214 | + } |
215 | + |
216 | + OptionSelector { |
217 | + id: sortBy |
218 | + StateSaver.properties: 'selectedIndex' |
219 | + model: ListModel { |
220 | + ListElement { label: 'Title'; role: 'title' } |
221 | + ListElement { label: 'Year'; role: 'year' } |
222 | + ListElement { label: 'Length'; role: 'duration' } |
223 | + } |
224 | + delegate: OptionSelectorDelegate { |
225 | + text: i18n.tr(label) |
226 | + } |
227 | + property string role: 'title' |
228 | + onDelegateClicked: { |
229 | + var selectedIndex = index |
230 | + role = model.get(selectedIndex).role |
231 | + // Work-around for sort.property not correctly re-sorting |
232 | + var m = movieList.model |
233 | + movieList.model = null |
234 | + movieList.model = m |
235 | + } |
236 | + } |
237 | + |
238 | + TextField { |
239 | + id: searchProducer |
240 | + StateSaver.properties: 'text' |
241 | + placeholderText: i18n.tr('Search Producer…') |
242 | + } |
243 | + |
244 | + OptionSelector { |
245 | + id: selectYear |
246 | + StateSaver.properties: 'selectedIndex' |
247 | + model: [ '*', 2005, 2006, 2007, 2008, 2009 ] |
248 | + delegate: OptionSelectorDelegate { |
249 | + text: modelData == '*' ? 'All Years' : modelData |
250 | + } |
251 | + property var year: model[selectedIndex] |
252 | + } |
253 | + } |
254 | + } |
255 | + } |
256 | +} |
257 | + |
258 | |
259 | === modified file 'src/query.cpp' |
260 | --- src/query.cpp 2014-03-13 19:31:58 +0000 |
261 | +++ src/query.cpp 2014-11-12 17:08:18 +0000 |
262 | @@ -17,7 +17,6 @@ |
263 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
264 | */ |
265 | |
266 | -#include <QDebug> |
267 | #include <QSqlQuery> |
268 | #include <QFile> |
269 | #include <QSqlError> |
270 | @@ -64,7 +63,11 @@ |
271 | return m_results.at(index.row()); |
272 | if (role == 1) // docId |
273 | return m_documents.at(index.row()); |
274 | - return QVariant(); |
275 | + |
276 | + int indexForRole = role - 2; |
277 | + QVariantMap result(m_results.at(index.row()).toMap()); |
278 | + QString field(m_index->getExpression().at(indexForRole).split(".").last()); |
279 | + return result.value(field); |
280 | } |
281 | |
282 | /*! |
283 | @@ -79,6 +82,13 @@ |
284 | QHash<int, QByteArray> roles; |
285 | roles.insert(0, "contents"); |
286 | roles.insert(1, "docId"); |
287 | + if (!m_index) |
288 | + return roles; |
289 | + |
290 | + // A role name for each field in the index |
291 | + uint fieldRole = 2; |
292 | + Q_FOREACH(QString field, m_index->getExpression()) |
293 | + roles.insert(fieldRole++, field.split(".").last().toUtf8()); |
294 | return roles; |
295 | } |
296 | |
297 | |
298 | === modified file 'tests/test-database.cpp' |
299 | --- tests/test-database.cpp 2014-11-07 14:30:31 +0000 |
300 | +++ tests/test-database.cpp 2014-11-12 17:08:18 +0000 |
301 | @@ -82,6 +82,21 @@ |
302 | query.setQuery(QStringList() << "2014*" << "basketball" << "linux"); |
303 | } |
304 | |
305 | + void testRoleNames() |
306 | + { |
307 | + Database db; |
308 | + Index index; |
309 | + index.setDatabase(&db); |
310 | + index.setExpression(QStringList() << "species" << "traits.spots"); |
311 | + Query query; |
312 | + query.setIndex(&index); |
313 | + QStringList expected(QStringList() << "docId" << "contents" << "species" << "spots"); |
314 | + Q_FOREACH(QString roleName, expected) |
315 | + QVERIFY2(query.roleNames().values().contains(roleName.toUtf8()), |
316 | + qPrintable(QString("Expected role name \"%1\" not in Query").arg(roleName))); |
317 | + QVERIFY(db.lastError().isEmpty()); |
318 | + } |
319 | + |
320 | void cleanupTestCase() |
321 | { |
322 | } |
323 | |
324 | === modified file 'tests/tst_query.qml' |
325 | --- tests/tst_query.qml 2014-03-13 19:31:58 +0000 |
326 | +++ tests/tst_query.qml 2014-11-12 17:08:18 +0000 |
327 | @@ -20,6 +20,8 @@ |
328 | import QtQuick 2.0 |
329 | import QtTest 1.0 |
330 | import U1db 1.0 as U1db |
331 | +import Ubuntu.Components 1.1 |
332 | +import Ubuntu.Test 0.1 |
333 | |
334 | Item { |
335 | width: 200; height: 200 |
336 | @@ -241,13 +243,79 @@ |
337 | signalName: "documentsChanged" |
338 | } |
339 | |
340 | -TestCase { |
341 | + U1db.Database { |
342 | + id: animals |
343 | + } |
344 | + |
345 | + // Note: docId's intentionally chosen for wrong alphabetic order |
346 | + U1db.Document { |
347 | + database: animals |
348 | + docId: 'k_cow' |
349 | + contents: { 'species': 'cow', 'traits': [ { 'spots': true } ] } |
350 | + } |
351 | + U1db.Document { |
352 | + database: animals |
353 | + docId: 'z_ant' |
354 | + contents: { 'species': 'ant' } |
355 | + } |
356 | + |
357 | + U1db.Document { |
358 | + database: animals |
359 | + docId: 'a_bee' |
360 | + contents: { 'species': 'bee' } |
361 | + } |
362 | + |
363 | + U1db.Document { |
364 | + database: animals |
365 | + docId: 'n_cat' |
366 | + contents: { 'species': 'cat' } |
367 | + } |
368 | + U1db.Index { |
369 | + id: bySpecies |
370 | + database: animals |
371 | + name: 'by-species' |
372 | + expression: [ 'species' ] |
373 | + } |
374 | + |
375 | + U1db.Query { |
376 | + id: allAnimals |
377 | + index: bySpecies |
378 | + query: [{ 'species': '*' }] |
379 | + } |
380 | + |
381 | + // To double-check failures aren't due to SortFilterModel itself |
382 | + ListModel { |
383 | + id: fakeAnimals |
384 | + ListElement { species: 'cow'; docId: 'k_cow' } |
385 | + ListElement { species: 'ant'; docId: 'z_ant' } |
386 | + ListElement { species: 'bee'; docId: 'a_bee' } |
387 | + ListElement { species: 'cat'; docId: 'n_cat' } |
388 | + } |
389 | + |
390 | + SortFilterModel { |
391 | + id: mixedAnimals |
392 | + model: allAnimals |
393 | + } |
394 | + |
395 | + SortFilterModel { |
396 | + id: sortedAnimals |
397 | + sort.property: 'species' |
398 | + sort.order: Qt.AscendingOrder |
399 | + } |
400 | + |
401 | + SortFilterModel { |
402 | + id: filteredAnimals |
403 | + filter.property: 'species' |
404 | + filter.pattern: /ant/ |
405 | + } |
406 | + |
407 | +UbuntuTestCase { |
408 | name: "U1dbDatabase" |
409 | when: windowShown |
410 | |
411 | function prettyJson(j) { |
412 | var A = JSON.stringify(j) |
413 | - if (A['0'] && A != '{}') { |
414 | + if (A != undefined && A['0'] && A != '{}') { |
415 | var A = '[' |
416 | for(var i in j) |
417 | A += JSON.stringify(j[i]) + ',' |
418 | @@ -260,6 +328,8 @@ |
419 | compare(a, b, msg, true) |
420 | } |
421 | |
422 | + TestUtil { id: util } |
423 | + |
424 | function compare(a, b, msg, willFail) { |
425 | /* Override built-in compare to: |
426 | Match different JSON for identical values (number hash versus list) |
427 | @@ -273,7 +343,12 @@ |
428 | console.log('Expected failure: %1%2 != %3'.arg(msg ? msg + ': ' : '').arg(A).arg(B)) |
429 | return |
430 | } |
431 | - fail('%5%1 != %2 (%3 != %4)'.arg(A).arg(B).arg(JSON.stringify(a)).arg(JSON.stringify(b)).arg(msg ? msg + ': ' : '')) |
432 | + if (!qtest_results.compare (false, |
433 | + msg ? msg : "Comparison failed", |
434 | + A + " ~ " + JSON.stringify(a), |
435 | + B + " ~ " + JSON.stringify(b), |
436 | + util.callerFile(), util.callerLine())) |
437 | + throw new Error("QtQuickTest::fail") |
438 | } |
439 | if (willFail) |
440 | fail('Expected to fail, but passed: %5%1 != %2 (%3 != %4)'.arg(A).arg(B).arg(JSON.stringify(a)).arg(JSON.stringify(b)).arg(msg ? msg + ': ' : '')) |
441 | @@ -359,5 +434,45 @@ |
442 | compare(allHeroesWithType.documents, ['dino', 'gokaiger', 'ooo', 'wizard'], 'go') |
443 | compare(allHeroesWithType.documents, allHeroesSeriesOnly.documents, 'roku') |
444 | } |
445 | + |
446 | + function test_7_sort_data () { |
447 | + return [ { tag: 'Reference ListModel', model: fakeAnimals }, |
448 | + { tag: 'U1db.Query', model: allAnimals } ] |
449 | + } |
450 | + |
451 | + function test_7_sort (data) { |
452 | + // Sanity checks |
453 | + compare(allAnimals.documents, ["a_bee", "k_cow", "n_cat", "z_ant"]) |
454 | + tryCompareFunction(function(){return mixedAnimals.get(0).docId}, "a_bee") |
455 | + |
456 | + sortedAnimals.model = data.model |
457 | + compare(sortedAnimals.count, 4, "number of animals") |
458 | + sortedAnimals.sort.property = 'species' |
459 | + compare(sortedAnimals.get(0).docId, "z_ant", "ants document sorted first") |
460 | + compare(sortedAnimals.get(0).species, "ant", "ants species sorted first") |
461 | + |
462 | + sortedAnimals.sort.order = Qt.DescendingOrder |
463 | + compare(sortedAnimals.get(0).species, "cow", "cow sorted first descending") |
464 | + |
465 | + sortedAnimals.sort.property = 'docId' |
466 | + sortedAnimals.sort.order = Qt.AscendingOrder |
467 | + compare(sortedAnimals.get(0).docId, "a_bee", "bee sorted first by docId") |
468 | + } |
469 | + |
470 | + function test_8_filter_data () { |
471 | + return [ { tag: 'Reference ListModel', model: fakeAnimals }, |
472 | + { tag: 'U1db.Query', model: allAnimals } ] |
473 | + } |
474 | + |
475 | + function test_8_filter (data) { |
476 | + // Sanity checks |
477 | + compare(allAnimals.documents, ["a_bee", "k_cow", "n_cat", "z_ant"]) |
478 | + tryCompareFunction(function(){return mixedAnimals.get(0).docId}, "a_bee") |
479 | + |
480 | + filteredAnimals.model = data.model |
481 | + compare(filteredAnimals.count, 1, "number of animals") |
482 | + compare(filteredAnimals.get(0).species, "ant", "ants species role available") |
483 | + compare(filteredAnimals.get(0).docId, "z_ant", "ants included in filter") |
484 | + } |
485 | } } |
486 |
PASSED: Continuous integration, rev:117 jenkins. qa.ubuntu. com/job/ u1db-qt- ci/47/ jenkins. qa.ubuntu. com/job/ u1db-qt- precise- amd64-ci/ 47 jenkins. qa.ubuntu. com/job/ u1db-qt- saucy-amd64- ci/49 jenkins. qa.ubuntu. com/job/ u1db-qt- trusty- amd64-ci/ 27 jenkins. qa.ubuntu. com/job/ u1db-qt- trusty- armhf-ci/ 27
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/u1db- qt-ci/47/ rebuild
http://