“What do I do with that lime?” or “The genesis and state of my cocktail book index”

Two martini glasses with a clear liquid and an olive in each, over a seamless grey/white background

A while ago, I bought an excellent book, called Drinking French, by David Lebovitz. It’s not strictly a cocktail book because there’s lots of stuff in it, but I’ll admit that I’ve only tested the cocktail recipes so far.

I have also, since then, bought another excellent book, called Cocktail Codex, also a staple next to my liquor cabinet.

These two books have a few common issues:

  • their (paper) index does not make it easy to search for a cocktail that have two distinct ingredients (apart from looking at the index and finding the intersection),
  • their index is not complete and, in particular, does not contain “trivial” ingredients such as lemon juice or simple syrup,
  • their index does not necessarily account for substitutions I may feel confident doing,
  • they don’t have a common index and I’d need to have a look at both to make decisions.

So I typically find myself in a situation where I have limes, and no idea what I can make with them, except it’d be nice to have something with gin. Also, I’m not that picky and probably if you give me a recipe with lemons, I’ll put my limes in it instead and call it good enough. So, the problem that I was trying to solve was: “given a set of cocktail ingredients, give me ideas for what I can make with them, allowing for some fuzziness in the exact research”.

With that problem in the back of my mind, roughly at the same time, I read Index, A History of the, by Dennis Duncan (a book about the art of indexing), and I became somewhat fascinated by Wikibase (the Mediawiki-base software backing Wikidata, which handles structured data). Things kind of clicked to “WHAT IF I re-indexed the books in a Wikibase instance, I added some structure to the ingredients for the fuzziness, and I made SPARQL queries to get exactly what I want?”

So I did that – I installed Wikibase, and I started re-indexing. I added some structure to the data by adding “subclasses of” and “instances of” and “can be substituted by” and “such as”, and it was glorious. Then I gave some thought about the exact query I was interested in, and I ended up with something along the lines of “given a list of ingredients, for each of them, get a list of substitutes, and give me a recipe that contains at least of one substitute of each ingredient of the list”, where “substitute” is defined as either the ingredient itself, something that is explicitly defined as a substitute, or something that is (transitively) either a refinement or a larger category of the ingredient. The reasoning there is that, if I input “London dry gin”, I want to get the recipes that have “gin” (without qualifier), but also the ones that have a specific brand of London dry gin.

I bundled a small Mediawiki extension called CocktailSearch to be able to query my database and, for a while, I was happy. Here’s an early version of that interface (I had added the page number in the meantime too!)

Screenshot of a Mediawiki interface showing the CocktailSearch extension, displaying results for a search for lime juice and Cointreau.

And then, doubts crept in. My Wikibase install was run via the Docker images. It worked pretty well, but I was a bit unhappy with running 9 Docker images, including one that kept restarting (I probably could have fixed that one, but eh), on a machine (my home Windows desktop computer) that wasn’t really suited for it. I’m not much of a system administrator and I have 0 confidence in my Docker skills in general: the whole setup was making me a bit nervous. Migrating my cocktail index to a more persistent setup became my new project.

I considered multiple options, including “just moving the Docker images to another machine”. I finally settled on trying to run Wikibase, without the Docker images, on a Raspberry Pi that we have laying around. I actually went pretty far in the installation, and I think it could have worked out. But again, I got really nervous about the durability of the setup – Raspberry Pis are not known for being particularly good at data persistence. On top of that, getting from something that “mostly works” to something that I could plug in and access three minutes later with everything running looked like a goal that I might be able to eventually reach, but software rot was a real concern. I felt stuck.

I talked about that with my husband, who pointed out that my usage of SPARQL was actually fairly limited (it is, in fact, limited to a single large query), that my data was actually very very small (maybe a thousand records), and that I could maybe… not use SPARQL, and then not need the whole Wikibase machinery either. I was not convinced at first, because killing your darlings is hard. I had invested quite a bit of fondness in that architecture, and I did like the idea of running mostly standard software. But it didn’t take that much thinking before I actually got excited about the idea of simplifying the whole project drastically.

So, I exported my data to a JSON file, and I started hacking some import script. I then looked at my imported data and at my SPARQL query, hacked some loops in PHP, and essentially called it a day: my SPARQL query and my PHP queries were returning the same results for my few test queries.

Then, came the question of completing the database – indexing takes time, and my base was (and is still) not complete (and, who knows – maybe I’ll index some more books later!). While I’m able to programmatically read the dumped Wikibase JSON, I’m definitely not able to write it by hand without a lot of tears; but continuing to run Wikibase just as an input interface felt a tad excessive. Hence, I transformed my JSON structures into a flat file format that I could easily write by hand and easily parse. I added a significant amount of validation to avoid typo-duplicates and missing item references, and I re-exported my JSON data into that new format. I double-checked that I wasn’t losing any information (I’m indexing a bit more than I need, technically, because I’m also taking notes on the glass type, for instance), and then I started trying to complete the file with new items.

I honestly thought I wouldn’t last three recipes without slapping some kind of interface/completion on that file format, but it’s going significantly better than I expected. I did modify the file format a bit to make it easier to edit manually, at the cost of reading through the file twice when parsing it (I can live with that; and if I couldn’t, I could optimize there, but why bother :P). It feels like it’s enough: I have a validation script that runs fast enough with good enough error messages that I can input things and correct them more quickly and more pleasantly than I did with the Wikibase interface.

Speaking of interface, I also slapped a small web interface on the script, so that I can search with completion and have a readable output. The search completion was also far less involved than I expected: turns out the <datalist> tag does exactly what I want, assuming I pass a list of ingredients (which I can get as “transitive subclasses and instances of the ingredient item”) to the generating HTML. And there, new interface, new results – with additional data entry done in the meantime too 🙂

Screenshot of a HTML table displaying search results for cocktails with limejuice and Cointreau

So anyway, that’s the genesis of the current version of my cocktail book index, which is now called LimeCockail in reference to my original “now what do I do with these limes”.

It does feel a bit of a convoluted path for something that, at the end of the day, is, like, 500 lines of PHP, give or take, but going through that path was very interesting for a variety of reasons. It did give me some hands-on experience with Wikibase (granted, without the issues that come with “running a public instance” 🙂 ), and the Wikibase RDF structure helped me define my structured data in a way that makes sense to me. I also stretched my (almost nonexistent) sysadmin muscles to try to make all of this work together, and I (re-)learnt a few things about the LAMP stack and ElasticSearch. I also got a bit more experience with SPARQL, and I touched jQuery for the first time in a long time to be able to hack the search components backed by Wikibase data. All in all, this project taught me a lot of things!

Now I just need to finish indexing the Codex… 🙂

Scavenger Hunt #37 – Box

A wooden manikin, looking perplexed, in front of an open black box.

For the Scavenger Hunt #37, I revisited the theme I had used in Scavenger Hunt #32 – the wooden manikins are back!

For “Box”, I wanted to go with a literal interpretation of “thinking outside the box” – so that’s exactly what I did. I was not sure I could find a box of the right size – until I got the idea of going for a lens box – of which I have kept at least the ones whose warranty is still running. The Fujifilm box is also kind of nice because it’s black – apart from the written stuff, that gets easily edited away.

Unedited version of the previous picture of a puzzled manikin in front of a black box. The crop and angle are a bit different, and the black box shows Fujifilm brand and information.
CameraPentax K-1 II
Lenssmc PENTAX-D FA MACRO 100mm F2.8 WR
Focal length100mm
F-NumberF/7.1
Exposure time1/100 s
ISO800

The pictures from my fellow Scavengers are here: Box.

Scavenger Hunt #37 – Keyhole

A wooden manikin seemingly spying at something through a keyhole, processed with vignetting, grain and blue tones.

For the Scavenger Hunt #37, I revisited the theme I had used in Scavenger Hunt #32 – the wooden manikins are back!

For “Keyhole”, I knew fairly early that I wanted to do something about looking through a keyhole. Before I committed to going for a manikin theme again, I was thinking of using a keyhole as a frame for something (not hugely original, but working with what I have). Instead, I went for the “spying” theme.

The challenge was to get the manikin at the “right” height with the “right” pose in front of the door – that took a bit of creative posing on top of a chair’s back, but eventually I managed to get a shot I could work with.

An unedited, very underexposed version of the previous picture of a spying manikin.
CameraPentax K-1 II
Lenssmc PENTAX-D FA MACRO 100mm F2.8 WR
Focal length100mm
F-NumberF/8
Exposure time1/80 s
ISO800

I went for a “noir-themed” processing – cold tones, strong vignetting and grain – and I’m quite happy with the final picture.

The pictures from my fellow Scavengers are here: Keyhole.

Scavenger Hunt #37 – Intentional Camera Movement

A wooden manikin, in a super hero pose, tilted slightly to the right, seemingly zooming into the image, with very visible movement trails.

For the Scavenger Hunt #37, I revisited the theme I had used in Scavenger Hunt #32 – the wooden manikins are back!

“Intentional Camera Movement” almost stumped me, because it didn’t feel obvious to make that work in a very static context such as manikins in a studio. Until I remembered that playing with the focal length of the lens while shooting was also intentional camera movement! I set up my camera on a Platypod and then experimented with multiple takes, some going from the smaller focal length to the longer, some trying to move as smoothly as possible, some making a few pauses during the course of the lens. I ended up choosing a continuous move from longer length to shorter length.

An unedited version of the previous image, where the manikin's movement is visible, but the manikin is clearly standing in front of a wall, which breaks the illusion.
CameraPentax K-1 II
LensPentax D FA 24-70mm F2.8
Focal length70 to 28 mm, continuous
F-NumberF/22
Exposure time3s
ISO100

I edited the picture to play with the “super hero zooming into the scene” aesthetics, in particular in the way I chose to tilt the image. I also processed the image to emphasize the movement trails. And I think it’s one of my favorite pictures for this Hunt 🙂

The pictures from my fellow Scavengers are here: Intentional Camera Movement.

Scavenger Hunt #37 – Monochrome

Two wooden manikins. One is sitting in a wooden bowl filled with elastic bands; the other is stirring the bowl with a wooden spoon.

For the Scavenger Hunt #37, I revisited the theme I had used in Scavenger Hunt #32 – the wooden manikins are back!

One of the words was “Monochrome”, with a specific “not black&white” constraint. Since I had a manikin theme, and my manikins are wooden, well, that gives me the palette for what I’m doing. So I started gathering everything that was vaguely wooden-colored in the apartment, and tried to assemble SOMETHING. My train of thoughts when building this image was “a manikin spa” – spas do have weird treatments going on, so why not an elastic-band based one for wooden manikins! I wanted to give the impression of relaxation for the one sitting, and of attending the other for the one standing. I’ve been told it could also look like the day after a student party – and I’m not disagreeing with that 🙂

Unedited version of the previous "manikin spa" image. It is very underexposed, and there's a lot to crop in the borders of the image.
CameraPentax K-1 II
LensPentax D FA 24-70mm F2.8
smc PENTAX-D FA 50mm F2.8 Macro
smc PENTAX-D FA MACRO 100mm F2.8 WR
Pentax DA 18-55mm 1:3.5-5.6 AL
Focal length63mm
F-NumberF/1.8
Exposure time1/100 s
ISO111

The pictures from my fellow Scavengers are here: Monochrome.

Scavenger Hunt #37 – Roots

The formulas "sqrt(3) = 1.732" and "sqrt(7) = 2.645", written with pink ink on overlapping sheets of paper, and where the square root signs are not written, but mimed by wooden manikins lying on the paper.

For the Scavenger Hunt #37, I revisited the theme I had used in Scavenger Hunt #32 – the wooden manikins are back!

The “Roots” image is the one where I expect the most “… I don’t get it”. I considered tree roots, hair roots – I had a vague concept for tree roots but that would be hell to process in the very little time I had left, and hair roots felt difficult to pull off with bald manikins (I do NOT have fond memories of making Spaghetti behave long enough to take pictures!). And then… square roots. Well well well! I could work with that!

I first posed both manikins in “square-root” shapes (… as best as I could – these are NOT the most flexible models). I initially wanted to use a single sheet of paper to give the rest of the context, but I had a scale issue, so I embraced the multiple sheets and made them as visible as I could. And then, square root of 3 is roughly 1.372 and square root of 7 is roughly 2.645, so I picked my prettiest pink pen and made that happen.

Shooting from above was a bit of an ordeal (and involved climbing on a chair with my camera, which I normally try to avoid.)

An unedited version of the previous image, showing manikins miming square root signs, uncropped and very underexposed.
CameraPentax K-1 II
LensPentax D FA 24-70mm F2.8
Focal length33mm
F-NumberF/6.3
Exposure time1/60 s
ISO800

Processing was mostly straightforward – make the sheet rectangular, crop, fix exposure, done.

The pictures from my fellow Scavengers are here: Roots.

Scavenger Hunt #37 – Tape

Two wooden manikins on all fours, busy rewinding a VHS tape in front of a TV showing an "end of programmes" test card.

For the Scavenger Hunt #37, I revisited the theme I had used in Scavenger Hunt #32 – the wooden manikins are back!

The word “Tape” is the one for which I went through the most different concepts over the timeframe of the Hunt. I started thinking about putting tape on a gift to close it; same thing with a moving box; I considered an audio tape (but I don’t think I have any in the apartment), I did A Lot of attempts at making a measuring tape work (either as “measuring furniture” or “tailor measurements”… nothing quite worked. And then, I remembered that I had a VHS tape somewhere!! (This tape is coming from 3D Construction Kit, for which we have a box… containing said intro video tape.)

I had the “rewinding” notion early on, but I added the blue blanket and the “end of programmes” image on the TV/tablet behind after a few first takes to help sell the “rewinding the video rental after having watched it” – how 90s of me.

A very underexposed and uncropped version of the previous image of manikins rewinding a VHS tape.
CameraPentax K-1 II
LensPentax D FA 24-70mm F2.8
Focal length31mm
F-NumberF/11
Exposure time1/40 s
ISO800

The pictures from my fellow Scavengers are here: Tape.

Scavenger Hunt #37 – Map

Two wooden manikins sitting on a Scotland map.

For the Scavenger Hunt #37, I revisited the theme I had used in Scavenger Hunt #32 – the wooden manikins are back!

I felt a bit stumped on the word “Map” – my first idea was to play around my manikins being lost and looking at a map – but making a manikin-sized map looked like it would be a hassle, especially for something that’d be readable as a map on a picture. So I went the other way around – and I decided to show the story of planning a travel to <searches the apartment for a map, any map> Scotland, and tada!

Two wooden manikins sitting on a Scotland map - very underexposed picture.
CameraPentax K-1 II
LensPentax D FA 24-70mm F2.8
Focal length68mm
F-NumberF/9
Exposure time1/60 s
ISO800

It took a little while to get the right frame and light, but once I had that, the edits were fairly straightforward.

The pictures from my fellow Scavengers are here: Map.

Scavenger Hunt #37 – Hygge

A wooden manikin looking cozy in a cow-spots-patterned blanket, holding a mug in one hand and a green book in the other.

For the Scavenger Hunt #37, I revisited the theme I had used in Scavenger Hunt #32 – the wooden manikins are back!

The picture for Hygge is the first one I shot, and the only one I had for a long time during this hunt (I shot 7 out of 10 pictures on the last week-end before the time limit, so… not exactly early.) Looking for inspiration, hygge seems to evoke coziness, chimney fires, hot cocoa and the like. I re-used a piece of fabric that I had bought for “Cow” in a previous Hunt (yay, prop re-use!) to serve a blanket, and I cut a small book in a Nespresso ad that I had received a couple of days earlier. I’m *particularly* proud of the mug: it’s actually the cap of the box of earbuds tips that came with my new-ish earbuds – I was looking for something in that direction, was considering buying some clay to make something the right size, and… tada! A bit of blue tack to hold the mug and the book, a bit of posing and quite a few takes later, I had my picture.

Unedited version of the previous picture; the colors are less vivid and the framing is a bit wider.
CameraPentax K-1 II
Lenssmc PENTAX-D FA MACRO 100mm F2.8 WR
Focal length63mm
F-NumberF/14
Exposure time6 s
ISO500

I processed with warm tones to give the possible impression of a fireplace in front of the manikin. The fabric and manikin were a bit same-ish in the initial processing, so I darkened the manikin to make it “pop” a bit more against the cow blanket.

The pictures from my fellow Scavengers are here: Hygge.

Scavenger Hunt #37 – Lost

A wooden manikin on all fours, with its arm stuck between the cushions of a sofa, probably looking for its keys.

For the Scavenger Hunt #37, I revisited the theme I had used in Scavenger Hunt #32 – the wooden manikins are back!

For “Lost”, I went for the pretty straightforward narrative of “having lost one’s keys (or phone, or whatever else) between the cushions of the sofa”. So this was essentially posing the manikin, shooting, basic processing, and done.

A unedited version of the previous picture, very underexposed and with a bluer tone.
CameraPentax K-1 II
Lenssmc PENTAX-D FA MACRO 100mm F2.8 WR
Focal length100mm
F-NumberF/8
Exposure time1/80 s
ISO800

The pictures from my fellow Scavengers are here: Lost.