Digging into Structured Data for Media on Commons

Commons: Not a tragedy

Commons. What Commons?

If you are a contributor to Wikipedia or one of the other Wikimedia projects, you probably already know. If you aren’t, even as a frequent Wikipedia reader (and aren’t we all?), you may not know that almost all of the photographs and other images in the articles you read are hosted in a media repository, commons.wikimedia.org.

These images are generally free to reuse, modify, and share, for any purpose including commercial use. You can also create an account there and upload your own photographs, as long as they have educational value.

Image from Commons

Searching for cats on Commons

Structured Data is GREAT

But let’s suppose you want to sort through the media for some reason; maybe you want to find all of the photographs of edible wild mushrooms in your region, with the name of each species. Or maybe you want cute cat pictures. You could try the search tab and hope for the best, but all of the information about an image is in a blob of unstructured text and formatted any sort of random way. You could look at the categories and see if you’re lucky enough that one category covers your needs, but odds are that for anything except the simplest of queries, you’ll come up empty-handed.

What about getting that information in a language other than English? Good luck with that; although the project is shared among speakers of many different languages, the predominant language of contribution and of category names is still English.

Suppose you want to monitor newly uploaded images for any of the above, via a script. How can you do that? With difficulty.

Until now.

Structured data allows contributors to add a caption or information about what is depicted, to each uploaded media file, in any language, or in multiple languages. While the file description is still an unstructured string of text per language, descriptions in multiple languages can be specified and descriptions can be extracted for a media file independently of anything else. The same holds true of captions, and more data is likely to be added in the future.

Why am I writing about this? I produce dumps, so what do I care?

Introducing Mediainfo entities

Ah ha!  Someday () we will be producing dumps of this data. You’ll have files that contain all Mediainfo entities, much as there are now files containing the various sorts of Wikidata entities (see ) for download every week. And since we’re going to be dumping it, we need to understand the data: what is its format? How can we retrieve it via the MediaWiki api? How can we set it?

Mediainfo entities are similar-but-different from Wikibase entities. They are similar in that they have a special format (json) and cannot be edited directly via the wiki ‘edit source’ or ‘edit’ tabs. They both rely on the Wikibase extension as well. But Mediainfo entities are not stored in a set of separate tables, as Wikibase entities are. Instead, Mediainfo entities are stored in a secondary slot of the revision of the File page.

Wait, wut? What are slots? And what the devil is a ‘secondary slot’?

A side trip to Multi-Content Revisions

Time for a crash course in ‘Multi-Content Revisions’, also known as MCR. Until last year, MediaWiki had the following data model, as tweaked for use at Wikimedia:

  • Each article, template, user page, discussion page, and so on, is represented by a record in the ‘page’ table.
  • Each edit to a page is represented by a new record in the revision table.
  • Once a revision is added to the table, it never changes. It can be hidden from public view but never really removed.
  • A revision contains a pointer to a record in the text table.
  • The text table record contains a pointer to a record in a blobs table on one of our external storage servers.
  • The blob record contains the (usually gzipped) content of the revision as wikitext or json or css or whatever it happens to be.

So: one revision, one piece of content. And for articles (and File pages), that means one blob of wikitext with whatever formatting various editors have decided to give it.

But suppose…. just suppose that we could attach pieces of data to that page, also editable by contributors, and that could be displayed in a nice table or some other good data display format. A caption for an image, who or what’s in the image, maybe the creation date, maybe the name of the photographer, maybe EXIF data right from the image. Wouldn’t that be nice? Imagine if all of that data was available via the MediaWiki api, or easily searchable. Wouldn’t that be just grand?

That’s what Multi-Content Revisions are all about. That ‘extra data’ has to live somewhere and still be attached to a revision. So: slots. The ‘main slot’ contains wikitext or json or css or whatever the page normally has, that a contributor can edit the usual way. ‘Secondary’ slots, as many as we define, can have other data, like captions or descriptions or whatever else we decide is useful.

Now each edit to a page is represented by a new record in the revision table, but a revision contains a pointer to one or more entries in the slots table.

Each slot table entry contains a field indicating which slot it’s for (main? some other one?) and a pointer to an entry in the content table.

Each content table entry contains a pointer to a record in the text table, but at some point it will likely point directly to a blob on one of our external storage servers.

Back to Mediainfo entities

A Mediainfo entity, then, is a kind of Wikibase entity, structured data, in json format, that is stored in the ‘mediainfo’ slot for revisions of File pages on Commons.

Structured data tab for a file

This is easier to wrap one’s head around with an example.

If you look at https://commons.wikimedia.org/wiki/File:Marionina_welchi_(YPM_IZ_072302).jpeg you can see below the image that there is a ‘Structured Data’ tab. ‘Items portrayed in this file’ has a name listed.

If you look at the edit history for the file, ‎you can see an entry with the following comment:

Created claim: depicts (d:P180): (d:Q5137114)

This means that someone entered the depiction information by clicking ‘Edit’ next to the ‘Items portrayed in this file’ message, and added the information. You can do the same, in any language, or you can modify or remove existing depiction statements.

JSON for “Depicts” data

Let’s see what the raw content behind a ‘depicts statement’ is.

We can get the content of a Mediainfo entity by providing the Mediainfo id to the MediaWiki api and doing a wbgetentities action. But first we need to get that id. How do we do that?

Here’s the trick: the Mediainfo id for a File page is ‘M’ plus the page id! So first we retrieve the page id via the api:

https://commons.wikimedia.org/w/api.php?action=query&prop=info&titles=File:Marionina_welchi_(YPM_IZ_072302).jpeg&format=json

There’s the page id: 82858744. So now we have the Mediainfo id M82858744 we can pass to wbgetentities:

https://commons.wikimedia.org/w/api.php?action=wbgetentities&ids=M82858744&format=json

And here’s the output, prettified for human readers like you and me:

{
  "entities": {
    "M82858744": {
      "pageid": 82858744,
      "ns": 6,
      "title": "File:Marionina welchi (YPM IZ 072302).jpeg",
      "lastrevid": 371114515,
      "modified": "2019-10-19T08:11:24Z",
      "type": "mediainfo",
      "id": "M82858744",
      "labels": {},
      "descriptions": {},
      "statements": {
        "P180": [
          {
            "mainsnak": {
              "snaktype": "value",
              "property": "P180",
              "hash": "bfa568e1a915cc36538364c66cbfeea50913feea",
              "datavalue": {
                "value": {
                  "entity-type": "item",
                  "numeric-id": 5137114,
                  "id": "Q5137114"
                },
                "type": "wikibase-entityid"
              }
            },
            "type": "statement",
            "id": "M82858744$21beba99-41e7-a211-66ac-1cacc78b806d",
            "rank": "preferred"
          }
        ]
      }
    }
  },
  "success": 1
}

You can see the depicts statement under ‘statements’, where ‘P180’ is the property ‘Depicts’, as seen on Wikidata. ‘Q5137114’ is Marionina welchi, as seen also on Wikidata. The P-items and Q-items are available from Wikidata by virtue of Mediainfo’s use of “Federated Wikibase”; see https://gerrit.wikimedia.org/r/plugins/gitiles/mediawiki/extensions/Wikibase/+/master/docs/federation.wiki for more on that.

Note!! Since the page id of the File page is effectively embedded in the Mediainfo text blob as stored in the database, any change to the page id can break things. See https://phabricator.wikimedia.org/T232087 for an example!

Raw content of “Depicts” statement

What is the raw content of the slot for that revision in the database, you ask? I’ve got a little script for that. (Link: https://github.com/apergos/misc-wmf-crap/blob/master/get_revision.py) Here’s what I get when I run the script:

{"type":"mediainfo","id":"M82858744","labels":[],"descriptions":[],"statements":{"P180":[{"mainsnak":{"snaktype":"value","property":"P180","hash":"bfa568e1a915cc36538364c66cbfeea50913feea","datavalue":{"value":{"entity-type":"item","numeric-id":5137114,"id":"Q5137114"},"type":"wikibase-entityid"}},"type":"statement","id":"M82858744$21beba99-41e7-a211-66ac-1cacc78b806d","rank":"preferred"}]}}

That’s right, all that mainsnak and snaktype and other stuff is right there in the raw slot content.

How statements work

A statement (in Wikidata parlance, a “claim”), is an assertion about a subject that it has a certain property (Pnnn) with a certain value. If the value turns out to be a person or a place or something else with a Wikidata entry (Qnnn), then that id can be used in place of the text value.

Here’s an example:

Boris Johnson holds the position (P39 “position held”) of Prime Minister of the UK (Q14211). But this hasn’t always been true and it won’t be true forever. This statement therefore needs qualifiers, such as: P580 (“start time”) with value 24 July 2019.

A “snak” (chosen as the next largest data item after “bit” and “byte”) is a claim with a property and value but no qualifiers.

In our case, an image can depict (P180) some person or thing (Q-id for the person/thing if there is one, or the name otherwise). Or it can have been created by (P170) some person. Or it can have been created (P571) on a certain date. The point is that statements (claims) of any sort can be added to a Mediainfo entity. However, some care should be taken before adding statements involving properties other than P180 and P170, preferably after discussion and agreement with the community. See https://commons.wikimedia.org/wiki/Commons_talk:Structured_data/Modeling for some of the discussion around use of properties for Commons media files.

For a note on the terminology “statements” vs. “claims”, see https://phabricator.wikimedia.org/T149410.

For more about snaks and snaktypes, see https://gerrit.wikimedia.org/r/plugins/gitiles/mediawiki/extensions/Wikibase/+/master/docs/json.wiki#261.

Looking at Captions

Let’s find a File which has a caption. I’ve already found one so we’ll just use it as an example:

https://commons.wikimedia.org/wiki/File:Stra%C3%9Fenbahn_Haltestelle_Freizeit-_und_Erholungszentrum-3.jpg

Underneath the image, in the ‘File Information’ tab, you can see the entry ‘Captions’, and there are some! If you select ‘See 1 more language’ you can see that there are two captions, one in English and one in German.

Let’s get the Mediainfo id for that file:

https://commons.wikimedia.org/w/api.php?action=query&prop=info&titles=File:Straßenbahn_Haltestelle_Freizeit-_und_Erholungszentrum-3.jpg&format=json

Great, it’s 83198284. Let’s plug M83198284 into our wbgetentities query:

https://commons.wikimedia.org/w/api.php?action=wbgetentities&ids=M83198284&format=json

{
  "entities": {
    "M83198284": {
      "pageid": 83198284,
      "ns": 6,
      "title": "File:Straßenbahn Haltestelle Freizeit- und Erholungszentrum-3.jpg",
      "lastrevid": 371115009,
      "modified": "2019-10-19T08:16:04Z",
      "type": "mediainfo",
      "id": "M83198284",
      "labels": {
        "en": {
          "language": "en",
          "value": "Tram stop in Berlin, Germany"
        },
        "de": {
          "language": "de",
          "value": "Straßenbahn Haltestelle in Berlin"
        }
      },
      "descriptions": {},
      "statements": []
    }
  },
  "success": 1
}

Captions are called ‘labels’ and are returned by language with a simple text value for each caption. What’s the raw data, you ask?

{"type":"mediainfo","id":"M83198284","labels":{"en":{"language":"en","value":"Tram stop in Berlin, Germany"},"de":{"language":"de","value":"Stra\u00dfenbahn Haltestelle in Berlin"}},"descriptions":[],"statements":[]}

Pretty much as we expect, the entity type and id are stored along with the labels, with one entry per language.

Looking at Descriptions

Just kidding!  More seriously, there’s a placeholder for descriptions but that’s due to be removed (see https://phabricator.wikimedia.org/T213502) since captions suffice.

Looking at anything else

I’ve got a script. It’s crap because all code is crap, and all code not written for production is especially crap, and code witten for personal use just to get the job done is extra especially crap. Nonetheless, I use this python script to get the Mediainfo entity for a given File on Commons: https://github.com/apergos/mw-scripts-crapola/blob/master/get_mediainfo.py and it gets the job done.

The Big Payoff: Search

Let’s find some images on Commons using this data. Go to the main page and let’s search for any media with a “depicts” (P180) statement, by entering “haswbstatement:P180” in the search bar.You can check the Structured Data tab below the image of any of the files in the result, and see what’s depicted.

But that’s not all! You can specify what you want depicted: haswbstatement:P180=Q146 will find any media file marked as depicting a… https://www.wikidata.org/wiki/Q146 just by entering it into the search bar.

And that’s still not all! You can specify that you want only those pictures with captions in English and that depict Q146’s by entering hasdescription:en haswbstatement:P180=Q146. Note!! Captions used to be specified by the keyword “hascaption” but this has been changed, though you may see it referenced in older documentation or blog posts.

But there’s more! You can specify that you want pictures that depict something, created by someone else, with captions in some languages but not others, and CirrusSearch will serve that right up to you. Try it by searching for haswbstatement:P170=Q34788025 haswbstatement:P180=Q158942 hasdescription:en -hasdescription:fr and check the results.

But… you guessed it, that’s still not all. You can search for all media files that have a specified text in the caption, in addition to any other search criteria! Try it by searching for incaption:dog hasdescription:fr and check any file in the results.

Bonus: Editing!

I’ve got a script. It just updates captions because the format of those is the easiest, but if you look at it and the api help docs you can figure out the rest.

Script: https://github.com/apergos/misc-wmf-crap/blob/master/glyph-image-generator/set_mediainfo.py

MediaWiki api help: https://commons.wikimedia.org/w/api.php?action=help&modules=wbeditentity

Further Reading

There’s lots more to explore, so here’s some links to get you started.

Credits

0 Responses to “Digging into Structured Data for Media on Commons”



  1. Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.





%d bloggers like this: