Using U1DB in ListViews in Ubuntu SDK apps

After explaining how to use U1DB to store simple bits of information in Ubuntu SDK apps, and saying that that caters for 80% of my need for data storage, I should explain the other thing I do, which is to store dynamic data; documents created from user data.

To understand more about how to retrieve data from U1DB through Indexes and Queries, you can read the core U1DB documentation. Its code examples are for the Python implementation, and QML works differently for creating documents (as we’ve seen, QML is declarative; there’s no code to write, you just describe a document and it all works), but indexing and querying documents have the same underlying philosophy regardless of implementation, and the core docs explain what an index is, what a query is, and how they work.

First, a simple code example.

import QtQuick 2.0
import Ubuntu.Components 0.1
import U1db 1.0 as U1db
import Ubuntu.Components.ListItems 0.1 as ListItem

MainView {
    width: units.gu(40)
    height: units.gu(71)

    /* ----------------------------------------------------
       Set up the U1DB database
       Declare a named document
       ---------------------------------------------------- */
    U1db.Database { id: db; path: "simpleu1dbdemo2.u1db" }
    U1db.Index {
        database: db
        id: by_type
        /* You have to specify in the index all fields you want to retrieve
           The query should return the whole document, not just indexed fields
           https://bugs.launchpad.net/u1db-qt/+bug/1271973 */
        expression: ["things.type", "things.placename"]
    }
    U1db.Query {
        id: places
        index: by_type
        query: ["*", "*"]
    }

    Page {
        title: "U1DB ListModel"

        Column {
            id: col
            width: parent.width
            spacing: units.gu(1)
            Label {
                width: parent.width
                text: "Enter a place to add to list"
                horizontalAlignment: Text.AlignHCenter
            }
            Rectangle {
                id: ta
                width: parent.width - units.gu(2)
                color: "white"
                height: inp.height * 2
                anchors.horizontalCenter: parent.horizontalCenter
                radius: 5
                TextInput {
                    id: inp
                    width: parent.width - units.gu(2)
                    anchors.centerIn: parent
                    onAccepted: inp.adddoc()

                    function adddoc() {
                        /* Indexes do not work on top-level fields. So put everything
                           in the document in a dict called "things" so that they're
                           not top-level fields any more.
                           https://bugs.launchpad.net/u1db-qt/+bug/1271972 */
                        db.putDoc({things: {type: "place", placename: inp.text}})
                        inp.text = ""
                    }
                }
            }
            Button {
                text: "Add"
                width: ta.width
                anchors.horizontalCenter: parent.horizontalCenter
                onClicked: inp.adddoc()
            }
        }
        ListView {
            anchors.top: col.bottom
            anchors.bottom: parent.bottom
            width: parent.width
            model: places
            clip: true
            delegate: ListItem.Standard {
                text: model.contents.placename
                control: Button {
                    text: "x"
                    width: units.gu(4)
                    onClicked: {
                        /* To delete a document, you currently have to set its 
                           contents to empty string. There will be db.delete_doc
                           eventually.
                           https://bugs.launchpad.net/u1db-qt/+bug/1243395 */
                        db.putDoc("", model.docId);
                    }
                }
            }
        }
    }
}

You type in a place name and say “Add”; it gets added to the list. The list is stored in U1DB, so it persists; close the app and open it again and you still have your place names. Click a place to delete it.

This covers almost all the remaining stuff that I need to do with data storage. There are a few outstanding bugs still with using U1DB from QML, which I’ve annotated in the source above, and at the moment you have to work around those bugs by doing things that you ought not to have to; once they’re fixed, this becomes more intuitive to use.