Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

clojure-variable-names for database_column_names

This is a "what's most idiomatic in Clojure" question.

I'm using Cassandra for my DB, with Alia as my Clojure driver (both Cassandra and Alia work phenomenally well -- couldn't be happier).

The problem is this: Cassandra uses underscores (not dashes) in column names, and Clojure prefers dashes to underscores. So "user-key" in Clojure is "user_key" in Cassandra. How best to handle the mapping of Cassandra column names to Clojure variables?

Because I'm using prepared statements for my CQL queries, I think the fact that column names contain underscores and not dashes is more than an implementation detail to be abstracted away -- I'm frequently putting CQL queries as strings into my Clojure code, and I think it's important to represent the CQL as it actually is. I have considered approaches that auto-magically translate dashes to underscores in query strings, so that there's a Clojure version of the CQL that gets mapped to the Cassandra version of the CQL, but this seems like an inappropriate level of abstraction. Besides you'd still need to use underscores when you ran CQL queries directly in Cassandra for troubleshooting, so you'd need to keep two different representations of column names in your head. Seems like the wrong approach.

The approach I've ended up taking is to perform the mapping in a Clojure destructuring map, like this:

(let [{user-key :user_key, user-name :user_name} 
    (conn/exec-1-row-ps "select user_key,user_name from users limit 1")] )

("conn/exec-1-row-ps" is my convenience function that just looks the CQL string up in a map, and uses the previously-prepared statement if present, or else prepares the statement and stashes it in the map, and then executes the prepared statement and returns the first row of the result set, or throws an exception if more than one row is returned).

if I use the more concise {:keys []} destructuring method, then I'm stuck with underscores in my Clojure variable names:

(let [{:keys [user_key user_name]} ...

That was the first approach that I tried, but it gets ugly very fast, as variable names with underscores seep through the code, and come head-to-head with ones with dashes. Confusing.

Having been confronted with this issue for a long time, doing the conversion in the destructuring map, where Clojure "variable-name" and Cassandra "column_name" are side-by-side feels like the best solution. It also lets me expand out short_col_nms to more-descriptive-variable-names when I want to.

This bears some resemblance to the mapping that Clojure does of underscores in filenames to dashes in namespaces, so it feels like there is some precedent for doing the mapping like this. In the filename/namespace case, Clojure does the mapping automagically, and so perhaps the direct analog would be a version of {:keys []} destructuring that mapped dashes to underscores.

I'm a relative newbie to Clojure, so I realize there may be better ways to do this. Hence my question.

One improvement that I've considered is writing a macro that builds the destructuring map dynamically at compile-time. But I don't know how to write a macro that operates that early in the compilation process.

like image 947
Chris Tennant Avatar asked Nov 27 '13 13:11

Chris Tennant


1 Answers

The camel-snake-kebab has a nice clean interface to these sort of conversions.

From the examples:

(use 'camel-snake-kebab)

(->CamelCase 'flux-capacitor)
; => 'FluxCapacitor

(->SNAKE_CASE "I am constant")
; => "I_AM_CONSTANT"

(->kebab-case :object_id)
; => :object-id

(->HTTP-Header-Case "x-ssl-cipher")
; => "X-SSL-Cipher"
like image 162
sw1nn Avatar answered Sep 18 '22 00:09

sw1nn