   (->> (select experiments (with cohorts) (fields :FILE [:cohorts.name :cohort]))
        (map dataset-transform)))
 
-(defn select-probes []
-  (select* probes))
-
-; The cast is required because group_concat converts to hex.
+; Intersect experiment samples with the given set of samples.
+(defn exp-samples-in-list [exp samps]
+  (select exp_samples (with samples) (fields :i :samples.sample)
+          (where  {:eid exp}) (where (in :samples.sample samps))))
+
+(defn sample-bins [samps]
+  (set (map #(quot (% :I) bin-size) samps)))
+
 (defn select-scores []
   (-> (select* probes)
-      (fields (raw "CAST(GROUP_CONCAT(`expScores` ORDER BY `i` SEPARATOR '') AS BINARY) AS `expScores`"))
-      (join scores)
-      (group :joins.pid)))
+      (fields :name [:scores.expScores :expScores] [:joins.i :i])
+      (join scores)))
+
+(defn with-bins [q bins]
+  (where q (in :i bins)))
 
 (defn for-experiment [q exp]
   (where q {:probes.eid exp}))

  (let [baos (java.io.ByteArrayOutputStream.)]
    (binary/encode (codec-length (count slist)) baos slist)
    (.toByteArray baos)))

(defn- load-probe [exp prow]
  (let [pid (insert-probe exp (:probe (meta prow)))

(defn for-experiment-named [q exp]
  (for-experiment q (subselect experiments (fields "id") (where {:file exp}))))

(defn exp-by-name [exp]
  (let [[{id :ID}]
        (select experiments (fields "id") (where {:file exp}))]
    id))

; XXX expand model for probes/genes??
(defn with-genes [q genes]
  (fields (where q {:probes.name [in genes]}) [:probes.name :gene]))

(defn do-select [q]
  (exec q))

; merge bin number and bin offset for a sample.
(defn- merge-bin-off [sample]
  (let [{i :I} sample]
    (assoc sample :bin (quot i bin-size) :off (rem i bin-size))))

(defn- bin-mapping [order {off :off sample :SAMPLE}]
  [off (order sample)])

(defn- pick-samples-fn [order [bin samples]]
  [bin (partial ashuffle-float
                (map (partial bin-mapping order) samples))])

; Take an ordered list of requested samples, a list of samples (with indexes) in the
; experiment, and generate fns to copy values from a score bin to an output array.
; Returns one function for each score bin in the request.

(defn- pick-samples-fns [s-req s-exp]
  (let [order (zipmap s-req (range))
        by-bin (group-by :bin s-exp)]
    (apply hash-map (mapcat (partial pick-samples-fn order) by-bin))))

; Take map of bin copy fns, list of scores rows, and a map of output arrays,
; copying the scores to the output arrays via the bin copy fns.
(defn- build-score-arrays [rows bfns out]
  (dorun (map (fn [{i :I scores :EXPSCORES gene :GENE}]
                ((bfns i) (out gene) scores))
              rows))
  out)

(defn- col-arrays [columns n]
  (zipmap columns (repeatedly (partial float-array n))))

; XXX Need to fill in NAN for unknown samples.
(defn genomic-read-req [req]
  (let [{samples 'samples table 'table columns 'columns} req
        eid (exp-by-name table)
        s-in-exp (map merge-bin-off (exp-samples-in-list eid samples))
        bins (map :bin s-in-exp)
        bfns (pick-samples-fns samples s-in-exp)]

    (-> (select-scores)
        (for-experiment eid)
        (with-genes columns)
        (with-bins bins)
        (do-select)
        (build-score-arrays bfns (col-arrays columns (count samples))))))

; doall is required so the seq is evaluated in db context.
; Otherwise lazy map may not be evaluted until the context is lost.
(defn genomic-source [reqs]
  (doall (map #(update-in % ['data] merge (genomic-read-req %)) reqs)))

(defn create[]
  (kdb/transaction
