Thursday, August 18, 2011

RDF Low Level Interaction in Abiword

I've recently mentioned creating and jumping to the RDF Links (xml:id to RDF bridging) in Abiword. I also had a work in progress dialog to allow SPARQL Query execution. Things are moving along quite nicely and the RDF subsystem continues to get stronger with a Triple Editor and updates to the SPARQL dialog shown below and execution support.




Shown below is the new RDF Editor dialog. The file menu allows you to import and export RDF/XML files into the document. The Triple menu has the same actions as the toolbar; new, copy, and delete. Shown at the bottom of the dialog is the total number of RDF triples for this document. Clicking on a cell in the table lets you edit it and you can use the existing URI prefixes if you like, for example rdf:type instead of the full URI. I have to allow configuration of those prefixes yet.

When you select some text in Abiword you can use Insert/RDF Link to create a new RDF link for that text. This is much like making a bookmark, hyperlink and other items.

The context menu for an RDF link allows you to edit the RDF associated with link. That window is shown below. There can be many RDF links in scope at any location in the document. Consider the case where a paragraph has RDF linked to it and a sentence and word does too. So when you bring up the RDF editor window from the context menu of an RDF link you get a combo box letting you select which links you want to see the RDF associated with.

By default the editor shows you all the RDF Links that are in scope where the cursor was located when you opened the dialog from the context menu. In this case widetime is an RDF link for the whole paragraph and wingb is for three words at the end of the paragraph. The dialog shows you the triples that link the subjects to this location: those with ?subject pkg:idref widetime. Selecting a different RDF link from the combo box restricts the triples shown to be only those associated with the RDF link you have chosen.

I decided to make the dialog automatically link any new triples you create or edit to the RDF link you have specified in the combo box. If there is more than one RDF link shown then the first link is used for new and updated triples.

This allows you to select some text, create an RDF link, right click the link and "Show RDF" from the context menu and start adding and editing triples and Abiword will automatically associate all those shown with the RDF link you have selected. Yay, full, low level RDF support with a GUI ;)

In case you have forgotten the scope of an RDF link in the document, the RDF editor will select the scope in the main document when you select a triple in the editor. I might make that functionality have a toggle button in the toolbar instead of making it happen automatically all the time.


One of the plugins that comes with Abiword is the "AbiCommand" which gives you a console interface to Abiword. You can start it from the shell using:

$ abiword --plugin AbiCommand
...
AbiWord command line plugin: Type "quit" to exit
AbiWord:>

I have added some new RDF commands to the AbiCommand plugin which allows fairly good interaction with RDF from the command line. The context-model commands allow you to set and interact with a submodel of all the RDF in the document. For example, only the RDF that is associated with a given xml:id. All updates to RDF happen via a mutation object. So you first use rdf-mutation-create, then add/remove as desired and complete things with rdf-mutation-commit. For those who are still reading now, see pd_DocumentRDF.h in the C++ source code and you'll notice createMutation() in the model, with add(), remove(), commit() and rollback() methods. The new commands in AbiCommand are shown below:

...RDF subsystem...      

Where a function reads RDF, it will try to use the RDF context model if it is set
Otherwise the entire RDF for the document is used.
An RDF context is obtained using rdf-set-context*
and cleared with rdf-clear-context-model

rdf-import <src> - load all RDF from an RDF/XML file at <src> into the document
rdf-export <dst> - save all document RDF to an RDF/XML file at <dst>
rdf-clear-context-model - RDF can at times use a context model which is a subset of
all the RDF associated with the document.
This command clears that and uses all the RDF again.
rdf-set-context-model-pos <pos> - Use a context model with the subset of RDF
associated with the given document position
rdf-set-context-model-xmlid <xmlid> [readxmlid1,readxmlid2]
- Use a context model with the subset of RDF
associated with the given document xml:id value
rdf-context-show-objects <s> <p> - Show the object list for the given subject,predicate pair
rdf-context-show-subjects <p> <o> - Show the subject list for the given predicate,object pair
rdf-context-contains <s> <p> <o> - True if the triple is there.
rdf-context-show-arcs-out <s> - Show the predicate objects associated
with the given subject
rdf-get-xmlids - Get a comma separated list of the xml:ids assocaited
with the current cursor location
rdf-get-all-xmlids - Get a comma separated list of all the xml:ids
rdf-get-xmlid-range <xmlid> - Show the start and end document position associated
with the given <xmlid>
rdf-movept-xmlid-start <xmlid> - Move the cursor location to the start of the range
for the given xml:id value
rdf-movept-xmlid-end <xmlid> - Move the cursor location to the end of the range
for the given xml:id value
rdf-uri-to-prefixed <uri> - Convert full uri to prefix:rest
rdf-prefixed-to-uri <uri> - Convert prefix:rest to full uri
rdf-size - Number of RDF triples for context
rdf-mutation-create - Start a RDF mutation for the document
rdf-mutation-add <s> <p> <o>- Add the given triple to the current mutation
rdf-mutation-remove <s> <p> <o>- Remove the given triple in the current mutation
rdf-mutation-commit - Commit current RDF mutation to the document
rdf-mutation-rollback - Throw away changes in current RDF mutation
rdf-execute-sparql - Execute SPARQL query against RDF context
rdf-xmlid-insert <xmlid> - Insert xml:id for current selection
rdf-xmlid-delete <xmlid> - Delete the xml:id from the document


Some examples of AbiCommand interaction are shown below to give a little idea of use. These are using the multi.odt from my plugtest github repository.

load       /tmp/multi.odt

rdf-export /tmp/output.rdf
rdf-import /tmp/extra.rdf

rdf-set-context-model-pos 1006
rdf-export /tmp/1006.rdf

rdf-set-context-model-xmlid wingb
rdf-export /tmp/wingb.rdf

rdf-clear-context-model
rdf-export /tmp/all.rdf

rdf-get-xmlid-range wingb

rdf-execute-sparql "prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
prefix foaf: <http://xmlns.com/foaf/0.1/>
prefix pkg: <http://docs.oasis-open.org/opendocument/meta/package/common#>
prefix geo84: <http://www.w3.org/2003/01/geo/wgs84_pos#>

select ?s ?p ?o ?rdflink
where {
?s ?p ?o .
?s pkg:idref ?rdflink .
filter( str(?rdflink) = \"wingb\" || str(?rdflink) = \"widetime\" )
}"


load /tmp/multi.odt
movept +27
selectstart
movept +4
rdf-xmlid-insert foo
save /tmp/updated.odt

load /tmp/multi.odt
rdf-context-contains uri:wingb rdf:type http://www.w3.org/2002/12/cal/icaltzd#Vevent
rdf-context-show-objects uri:wingb rdf:type
rdf-context-show-subjects rdf:type http://www.w3.org/2002/12/cal/icaltzd#Vevent
rdf-context-show-arcs-out uri:wingb



Tuesday, August 2, 2011

RDF Linking in Abiword

Another minor step forward in the quest to make the RDF facility in ODF more useful to document authors... Abiword can now create links to RDF and you can jump to these links in a similar manner to how bookmarks work. The upside to using RDF links over bookmarks is that you can associate meaning with the RDF links. So for example, the text "Barry" can be associated with his vcard and possibly normal work geolocation.

Making a new RDF link is just like inserting a bookmark:
And the "Go To..." dialog now offers RDF links as first class citizens. I did a little tweaking to this goto window while I was at it; moving things into a paged configuration and abstracting out some common code into utility functions.

On the API front, there are now STL like iterators for the RDF and that theme will be present in the query results engine and perhaps also in the arcsOut() API. Speaking of querying, the window for SPARQL is coming along. I'll start working on the actual query execution shortly. Notice that the RDF triples are shown with namespaces in effect so you get something more readable.

As I mentioned in my previous post, the purple links can be turned on and off to highlight parts of the document with RDF associated. Using a special menu item you can pull up the SPARQL query dialog with a preformed query to show just the RDF associated with the current cursor location.

I admit that some of this is quite low level, for example directly inspecting the triples for the cursor position. But full disclosure isn't a bad thing right? I syndicated this to planet KDE because Calligra handles RDF too. Hopefully posts about RDF are interesting to hackers regardless of the desktop platform :)

Tuesday, July 19, 2011

Abiword & RDF Gusto

I recently blogged about updating Calligra to improve its RDF support and bring back support for viewing and editing location information using Marble. A computer loves RDF because it is nice and verbose and allows low level unambiguous expression of semantics in a format that a machine can work with. For a human however, some might find having long descriptors, schemas and the like just to say "meet me at the Mall" a little tedious. One of the many challenges that I see for office applications wanting to offer RDF to the user is making it visible in a subtle way.

Abiword can now colour code parts of the document which have RDF associated with them and tell you how that association is formed, and how much RDF is linked at any point. In the below, the purple text has some RDF associated, and the purple "Mark" I have the mouse pointer on so it shows the bubble text letting you know how the RDF is attached and how much of it there is.

In the future I of course want to let you know more; is the RDF location, contact, event, or related to another domain. It would also be nice to highlight RDF only for types. So, for example if you are interested only in the times that trains leave then highlight departure logistics in bold red. The computer knows what you mean too, so might also want to offer a menu button to check if the train is on time or not.

Being able to highlight like this is a good start because it allows users who are unfamiliar with the document the chance to know exactly where there might be RDF "hiding".

Friday, July 15, 2011

Calligra & RDF Smaak

As some readers will already know, the ODF document standard has support for including one or more RDF/XML files. I have made a few such files, and will grow that collection over time. The weekend hike document cites a few people, links some to a location and also cites a time and place. While one could just say "Dom Plein" or "11 am Wednesday" these purely text references are subjective and require a human to read them.

On the other hand, the location could have an exact bounding polygon or point with digital longitude and latitude information. A computer is all to happy to use that precise description to offer maps and "how to get there from here" type information. And the 11am is of course dubious because it doesn't link to a timezone, a human will know we are not talking UK time or the ACDT timezone. But that requires inference from the cited location and knowledge of which Plein that is, or rather, which timezone that Plein is in.

I did a little hacking to freshen up some of the RDF code in Calligra and bring back optional support for using Marble to show and edit location information. The below is the weekend hike example from the github above. James, Joyce, and Mark all have contact RDF associated, with Mark also giving his location. The "next weekend" at the end of the first paragraph has both a time and place associated. In the screenshot I've opened up the place to edit it. Rather simple to drag a map around and click OK than to know the digital coordinates right of the top of your head ;)


I was hacking on fixing the same bug as boemann on #calligra, which is strange for me as I normally don't overlap on things. It seems along the way setCanvas() was called again so I removed my explicit view passing stuff in the update I just git pushed. It looks like the docker was fixed correctly by somebody else in the end. My SPARQL updates should still help. It has been a while since I hacked on this codebase, and I have to thank the Calligra guys for being so welcoming and having such a fun contribution environment! I'm fortunate to be able to hack on two projects, Abiword and Calligra, which are both so welcoming :)

Monday, July 11, 2011

RDF in ODF: Abiword & Calligra


RDF has been slowly making it's way into Office applications. The ODF standard includes support for shipping RDF/XML file(s) inside the zip file that is an odt file. This RDF can also be linked to particular part(s) of the document text so that you and your computer both know where the RDF is most relevant. For example, if "Fred" in the document has his phone number, location, and cake preference in RDF, that can all be linked just to the four characters "Fred" so that it all makes sense. Strange as it might be, not everybody likes Baumkuchen, and it is fairly likely not to be relevant to a stock quote in another part of the document.

RDF has spread to OpenOffice, abiword, KOffice, and Calligra. All of these applications can read and write RDF in text documents. The later two also include a GUI to allow you to query, inspect, and update the RDF. Since I'm hacking on abiword, I've been throwing around how to best expose RDF to the person using abiword for document editing...

First, this is what Calligra does. The main document window includes an RDF docker which shows you the high level "Semantic objects". These are things which make use of many RDF triples to present a single object type such as a contact, calendar event, location, or explicit train trip. Note that the RDF docker only shows you the semantic objects for the RDF which is relevant to the current document cursor position.

The Document Information window also lets you get at all of the RDF which ships with an ODF file. The Semantic tab is very similar to the RDF docker but shows all the Semantic Objects regardless of where they are relevant in the document (if at all). As you can see below, editing the "Dan" person semantic object you can set their name, nickname, phone number, and homepage. Of course, more information is relevant to people and this whole section should be expanded to cater for that. And yes, for Calligra having a good hookup to Akonadi would be of great use for all.

Contacts use the FOAF RDF schema in Calligra. This allows not only contact information but also the relations between contacts to be expressed. FOAF is about Friends of a Friend after all. Looking at the above you might think name, phone etc are each going to be a triple in the RDF from the document. The triples tab lets you get at that lower level RDF goodness as shown below. A few things to note; while RDF is triples, each object has a type (is it a chunk of text or a link to another subject), and since there are many possible "files" the RDF/XML came from that is tracked for each triple so they can go back there too on save.


Notice in the above that prefixes are included in the subject, predicate, and object columns. This is an attempt to make the raw RDF less verbose and somewhat simpler to handle. The namespaces tab lets you set these up. Any namespaces that are used in the RDF/XML from the ODF file are automatically added and used for you.

The stylesheets tab I'll cover at another time. The SPARQL tab lets you run a query against all the RDF for the document. The one I've run here is the default one that Calligra shows you, which will select all the triples from the document without restriction. The subject, predicate, and object resulting are shown in the bottom half of the window.


I was thinking about all of this recently because I'm now looking to add GUI stuff to abiword to allow RDF interaction. The first idea was to simply add a "edit RDF" context menu item to allow you to associate one or more triples with the cursor position or current selection. The ability to define and reuse namespaces would also help to make such a dialog less painful to use. This brings the design close to the combined "Triples" and "Namespaces" tabs of the Calligra Document Information window. This might be OK for determined users who already really, really know they want to do these things. But I tend to think there are more folks who could take advantage of using RDF but not necessarily care about it.

Simplicity for users was the driving force behind the design of Semantic Objects and the use of Drag and Drop to and from other applications to create and harvest RDF data. I think it is much simpler to grab the "Fred" contact from Evolution and drop it into the document than to work out that you want to use FOAF and the exact predicates to create a well defined RDF graph for the Fred contact and then copy and paste each of those pieces of data individually.

One might like to consider the Triples+Namespaces as a special type of Semantic Object, a "raw" object if you will. This brings together the design of the advanced and user friendly interaction into a single dialog. As the namespaces are likely to have whole document scope they can be setup and edited elsewhere. Unfortunately I had a bit of trouble working out how to populate a tree or list in glade-2 or glade-3 for mock ups, so these are gimped a bit too.

The dialog below is a semantic object editor with the advanced tab allowing raw interaction. As there can be zero or more semantic objects of a given type in scope at any point there is a list on the left side allowing you to choose which object of a type to view. Perhaps that should be a drop down list at the top of the tab to save screen space.

The email and VoIP links should start a new message or request a phone call with the person respectively. Such actions should also be available without getting to the editor itself. My current plan is to have the advanced tab allow interaction with the raw triples. Remember though that triples carry type information, possibly extra context, and/or perhaps a range of the revisions in a change tracked document that the triple is valid for. So its by no means just a list with three columns as the name triples might at first imply.

A somewhat problematic first blush at this gives the below. I'm thinking that the subj, pred, and object strings can be namespace:foo strings, possibly with some completion for known namespaces like foaf, et al. The type is fairly OK as these are fixed and mainly URI or Object.


The revision range selection is a real challenge. This might become some sort of date range bar line the timeline or timeplot from the simile widgets. The trick as is usual is extrapolating the extra dimension from what is in it's vanilla sense a linear one dimensional data set ( time, revision ). Though having the revisions and their descriptions in the top half of the timeline and the ability to pan and zoom seeing a density plot in the lower half would work for starters.


I'm thinking that as well as showing you all the triples that maybe allowing simple one or two line SPARQL to be run to find the triples to edit would be preferable. Perhaps it doesn't add much for a small document range with only 20 triples associated, but to use the dialog on the whole document too, you might want to limit triples to "current revision" and foaf related only for example. Using a triple list allows you to sort by column and search, but such a search could also be performed with relatively simple SPARQL. And normally, and extremely unfortunately, one normally doesn't get to stable sort lists by 2+ columns. A limitation I try to avoid inflicting.

So in summary, raw triple editing can be just an advanced semantic object. The list of semantic objects should be able to be found from a document position (cursor) or arbitrary begin-end range. The later catering for whole document RDF editing as a special case. For contacts there might be one or more semantic objects for any doc position or range, but there will only be one raw-triple semantic object for any range.

Though I'm still chucking around how to make the query/edit part most convenient for users for the raw triples semantic object.

Saturday, June 18, 2011

Libferris for Fedora 15

My openSUSE Build Service project now has rpm files for Fedora 15 for the libferris chain including gevas and the ego file manager. This includes the recently mentioned updates to postgresql indexing allowing for indexed resolution of some regex queries (2ms query time on 200+k files).

During the updates, I found the normal little things such as gcc being more selective about what it likes to compile and treated these cases. The main glitch I found was in the @F15/../gtk/gtkmain.h header there is a prototype which takes a "GModule *module" argument. This means I had to include gmodule.h before gtk.h to get that type in scope otherwise things failed to compile.

The Fedora 15 packages have support for printing with Qt, so one can echo foo > printer://default/ or "cp" an image to the printer. Scanning support will pop up in a future build once the few tiny changes to ksane I sent in trickle through to the shipped KDE release in Fedora.

Perhaps soon I'll make Phonon available as a filesystem too so one can cp an ogg file to sound://default to play it. Seeking will be interesting, as it's not normally a common case to start copying a file at a given offset. But that smells like a seek during playback to me. I'm looking forward to making a QML audio player, with index-search and playback already filesystems the heavy lifting will have already been done :)

Monday, May 2, 2011

Searching again (400ms -> 2ms)

The problem statement: you want to search for a file using any arbitrary substring contained in the file url. This is deceptively hard, if you think in a relational db way, you want something "like %foo%" where the leading percent will force indexes to be considered redundant and a sequential scan to ensue. If you try instead to break up the file's URL into words and use a full text indexing solution you will find your old friend the leading percent again, like here for Lucene.

One easy way to use full text indexing style and allow substrings is to cheat. First disable word stemming (ie, dont turn diving and dive into a stemmed "dive" word). Then for each word to be added to the full text index, shift it left from 1..10 times. So wonderful, onderful, nderful, derful, erful etc are all added as words for the same URL. This is most simply accomplished using a postgresql function to do the shifting.

Then when you search for a substring foo you want to find to_tsquery('simple,'foo:*') as a fulltext query on your url column. As prefix searches are allowed in fulltext index engines like Lucene and postgresql's TSearch2 engine, this evaluates quite quickly.

For 200,000 URLs when using the raw regex match "~" in postgresql you might see 400ms evaluation times, the same data using a gin index on to_tsvector('simple',fnshiftstring(url)) might return in 2-3ms. Of course the index can't always handle your regular expression, but if you can tease out substrings which must be present from your regular expression, a shifted gin index could drop evaluation times for you. eg, .*[Ff]oob([0-9]... could use a shifted search for "oob" as a prefilter to full regular expression evaluation.

Getting down to a few ms evaluation allows GUIs to return some sample results as you type in your regular expression. This speed up will be available in 1.5.6+ of libferris.

A while ago I released an engine for maemo with statistical spatial indexing for regular expression evaluation. It's an interesting problem IMHO, and its also a very common one.