 (defn- insert-unique-scores-fn [writer]
   (memoize-key (:key writer) (partial insert-scores-block writer)))
 
-(defn- load-probe [writer insert-scores-fn exp row]
-  (let [pid ((:insert-probe writer) exp (:field row))
+(defn- load-field [writer insert-scores-fn exp row]
+  (let [field-id ((:insert-field writer) exp (:field row))
         blocks (:data row)]
     (doseq [[block i] (mapv vector blocks (range))]
-      (let [sid (insert-scores-fn block)]
-        ((:insert-join writer) pid i sid)))
-    pid))
+      (let [score-id (insert-scores-fn block)]
+        ((:insert-field-score writer) field-id i score-id)))
+    field-id))
 
 (defn- inferred-type
   "Replace the given type with the inferred type"
   [fmeta row]
   (assoc fmeta "valueType" (:valueType row)))
 
-(defn- load-probe-feature [writer insert-scores-fn exp acc row]
-  (let [pid (load-probe writer insert-scores-fn exp row)]
+(defn- load-field-feature [writer insert-scores-fn exp acc row]
+  (let [field-id (load-field writer insert-scores-fn exp row)]
     (if-let [feature (:feature row)]
-      (cons [pid (inferred-type feature row)] acc)
+      (cons [field-id (inferred-type feature row)] acc)
       acc)))
 
-(defn- insert-exp-sample [eid sample i]
-  (insert exp_samples (values {:experiments_id eid :name sample :i i})))
-
-(defn- load-exp-samples [exp samples]
+(defn- insert-exp-sample [dataset-id samp i]
+  (insert sample (values {:dataset_id dataset-id :name samp :i i})))
+
+(defn- load-samples [exp samples]
   (dorun (map #(apply (partial insert-exp-sample exp) %)
               (map vector samples (range)))))
 
 (defn- encode-scores [writer row]
   (mapv (:encode writer) (partition-all bin-size row)))
 
-; insert matrix, updating scores, probes, and joins tables
+; insert matrix, updating scores, fields, and field-score tables
 (defn- load-exp-matrix [exp matrix-fn writer]
   (let [{:keys [samples fields]} (matrix-fn)]
-    (load-exp-samples exp samples)
+    (load-samples exp samples)
     (let [scores-fn (insert-unique-scores-fn writer)
-          loadp (partial load-probe-feature writer scores-fn exp)
+          loadp (partial load-field-feature writer scores-fn exp)
           fields (chunked-pmap #(assoc % :data (encode-scores writer (:scores %))) fields)
-          probe-meta (reduce loadp '() fields)]
-      #(load-probe-meta probe-meta))))
+          field-meta (reduce loadp '() fields)]
+      #(load-probe-meta field-meta))))
 
 
 ; XXX Update to return all samples in cohort by merging
-; exp_samples.
+; sample.
 ;(defn- cohort-sample-list [cid sample-list]
 ;  (select samples (fields :id :sample)
 ;          (where {:cid cid :sample [in sample-list]})))

; Table models
;

(declare experiment_sources features probes scores experiments exp_samples)

(defentity cohorts)
(defentity sources)

(defentity experiments
  (many-to-many sources :experiment_sources)
  (has-many exp_samples)
  (has-many probes))

(defentity exp_samples
  (belongs-to experiments))

(declare score-decode)

(defn- cvt-scores [{scores :EXPSCORES :as v}]
  (if scores
    (assoc v :EXPSCORES (float-array (score-decode scores)))
    v))

(defentity probes
  (has-one features)
  (many-to-many scores :joins  {:lfk 'pid :rfk 'sid})
  (belongs-to experiments {:fk :eid})
  (transform cvt-scores))

(defentity scores
  (many-to-many probes :joins  {:lfk 'sid :rfk 'pid}))

(defentity joins)

;
; Table definitions.
;

; Note H2 has one int type: signed, 4 byte. Max is approximately 2 billion.
(def probes-table
  ["CREATE SEQUENCE IF NOT EXISTS PROBES_IDS CACHE 2000"
   "CREATE TABLE IF NOT EXISTS `probes` (
   `id` INT NOT NULL DEFAULT NEXT VALUE FOR PROBES_IDS PRIMARY KEY,
   `eid` INT NOT NULL,
   `name` VARCHAR(255),
   UNIQUE(`eid`, `name`),
   FOREIGN KEY (`eid`) REFERENCES `experiments` (`id`))"])

(def scores-table
  ["CREATE SEQUENCE IF NOT EXISTS SCORES_IDS CACHE 2000"
   (format "CREATE TABLE IF NOT EXISTS `scores` (
           `id` INT NOT NULL DEFAULT NEXT VALUE FOR SCORES_IDS PRIMARY KEY,
           `expScores` VARBINARY(%d) NOT NULL)" score-size)])

(def join-table
  ["CREATE TABLE IF NOT EXISTS `joins` (
   `pid` INT,
   `i` INT,
   `sid` INT,
   UNIQUE (`pid`, `i`))"])

(def cohorts-table
  ["CREATE TABLE IF NOT EXISTS `cohorts` (


  ([mname files metadata matrix-fn features force]
   (kdb/transaction
     (let [exp (merge-m-ent dataset-meta mname metadata)
           files (map fmt-time files)]
       (when (or force
                 (not (= (set files) (set (related-sources dataset exp)))))
         (clear-by-exp exp)
         (load-related-sources
           dataset_source :dataset_id exp files)
         (table-writer *tmp-dir*
                       (partial load-exp-matrix exp matrix-fn)))))))

; XXX factor out common parts with merge, above?
(defn del-exp [file]
  (kdb/transaction
    (let [[{exp :ID}] (select dataset (where {:file file}))]
      (clear-by-exp exp)
      (delete dataset (where {:id exp})))))

;
; probemap routines
;

(defn- add-probemap-probe [pmid a-probe]
  (let [pmp
        (insert probe (values {:probemap_id pmid
                               :name (a-probe :name)}))]
    (insert probe_position (values {:probemap_id pmid
                                    :probe_id pmp
                                    :bin (calc-bin
                                           (a-probe :chromStart)
                                           (a-probe :chromEnd))
                                    :chrom (a-probe :chrom)
                                    :chromStart (a-probe :chromStart)
                                    :chromEnd (a-probe :chromEnd)
                                    :strand (a-probe :strand)}))
    (dorun (map #(insert probe_gene (values {:probemap_id pmid
                                             :probe_id pmp
                                             :gene %}))
                (probe :genes)))))

; kdb/transaction will create a closure of our parameters,
