Merge lp:~kalikiana/u1db-qt/indexRoles into lp:u1db-qt

Proposed by Christian Dywan on 2014-03-19
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
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Approve on 2014-11-12
Benjamin Zeller 2014-03-19 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.

To post a comment you must log in.
Andrew Hayzen (ahayzen) wrote :

FYI the branch which I was having issues sorting can be found here [0].

0 - https://code.launchpad.net/~andrew-hayzen/+junk/u1db-sortfiltermodel-recent-test

lp:~kalikiana/u1db-qt/indexRoles updated on 2014-05-21
118. By Christian Dywan on 2014-05-21

Add sort and filter unit test cases and movies example

lp:~kalikiana/u1db-qt/indexRoles updated on 2014-05-22
119. By Christian Dywan on 2014-05-22

Build and examples should depend on UI toolkit plugin

lp:~kalikiana/u1db-qt/indexRoles updated on 2014-11-12
120. By Christian Dywan on 2014-11-12

Merge lp:u1db-qt

121. By Christian Dywan on 2014-11-12

Add C++ unit test checking role names of Query

PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
lp:~kalikiana/u1db-qt/indexRoles updated on 2014-11-12
122. By Christian Dywan on 2014-11-12

Work-around for sort.property not correctly re-sorting

PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)

Unmerged revisions

122. By Christian Dywan on 2014-11-12

Work-around for sort.property not correctly re-sorting

121. By Christian Dywan on 2014-11-12

Add C++ unit test checking role names of Query

120. By Christian Dywan on 2014-11-12

Merge lp:u1db-qt

119. By Christian Dywan on 2014-05-22

Build and examples should depend on UI toolkit plugin

118. By Christian Dywan on 2014-05-21

Add sort and filter unit test cases and movies example

117. By Christian Dywan on 2014-03-19

Expose index fields as role names

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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

Subscribers

People subscribed via source and target branches

to all changes: