 (defn- lhs-upsert [[old-index & rhs-indeces] ks v join-fn]
   (let [new-index (mapv (fn [index key] (group index (key v) v)) old-index ks)
 	new-indeces (apply vector new-index rhs-indeces)]
-    (println "JOINS: " (derive-joins v rhs-indeces ks)		;TODO: what should we do with this ?
+    (println "LHS JOINS: " (derive-joins v rhs-indeces ks join-fn)) ;TODO: what should we do with this ?
     new-indeces))
 
 ;;; TODO
-(defn- lhs-delete [old-indeces i k v])
-(defn- lhs-upserts [old-indeces i k vs])
-(defn- lhs-deletes [old-indeces i k vs])
+(defn- lhs-delete [old-indeces i ks v join-fn])
+(defn- lhs-upserts [old-indeces i ks vs join-fn])
+(defn- lhs-deletes [old-indeces i ks vs join-fn])
 
 (defn- lhs-view [indeces i keys join-fn]
   (reify
    View
    ;; singleton changes
    (on-upsert [_ upsertion] (swap! indeces lhs-upsert keys upsertion join-fn) nil)
-   (on-delete [_ deletion]  (swap! indeces lhs-delete i keys deletion) nil)
+   (on-delete [_ deletion]  (swap! indeces lhs-delete keys deletion join-fn) nil)
    ;; batch changes
-   (on-upserts [_ upsertions] (swap! indeces lhs-upserts i keys upsertions) nil)
-   (on-deletes [_ deletions]  (swap! indeces lhs-deletes i keys deletions) nil)))
+   (on-upserts [_ upsertions] (swap! indeces lhs-upserts keys upsertions join-fn) nil)
+   (on-deletes [_ deletions]  (swap! indeces lhs-deletes keys deletions join-fn) nil)))
 
 ;; TODO: return a pair - first is notification fn, second is new-indeces - use swap*! to apply
 
-(defn- rhs-upsert [old-indeces i k v]
-  (let [old-index (nth old-indeces i)
-	key (k v)
+(defn- rhs-upsert [old-indeces i lhs-key rhs-ks v join-fn]
+  (println "rhs-upsert: " old-indeces i rhs-ks v join-fn)
+  (let [
+	old-index (nth old-indeces i)
+	key (lhs-key v)
 	old-value (or (old-index key) [])
 	new-value (conj old-value v)
 	new-index (assoc old-index key new-value)
-	new-indeces (assoc old-indeces i new-index)]
+	new-indeces (assoc old-indeces i new-index)
+	lhs-indeces (first old-indeces)
+	lhs-index (nth lhs-indeces (dec i))
+	lhses (key lhs-index)
+	rhs-indeces (rest new-indeces)
+	joins (mapcat (fn [lhs] (derive-joins lhs rhs-indeces rhs-ks join-fn)) lhses)
+	]
+    (println "RHS JOINS: " joins)
     new-indeces
     ))
 
 ;;; TODO
-(defn- rhs-delete [old-indeces i ks v])
-(defn- rhs-upserts [old-indeces i ks vs])
-(defn- rhs-deletes [old-indeces i ks vs])
-
-(defn- rhs-view [indeces i keys]
+(defn- rhs-delete [old-indeces i ks v join-fn])
+(defn- rhs-upserts [old-indeces i ks vs join-fn])
+(defn- rhs-deletes [old-indeces i ks vs join-fn])
+
+(defn- rhs-view [indeces i key keys join-fn]
   (reify
    View
    ;; singleton changes
-   (on-upsert [_ upsertion] (swap! indeces rhs-upsert i keys upsertion) nil)
-   (on-delete [_ deletion]  (swap! indeces rhs-delete i keys deletion) nil)
+   (on-upsert [_ upsertion] (swap! indeces rhs-upsert i key keys upsertion join-fn) nil)
+   (on-delete [_ deletion]  (swap! indeces rhs-delete i keys deletion join-fn) nil)
    ;; batch changes
-   (on-upserts [_ upsertions] (swap! indeces rhs-upserts i keys upsertions) nil)
-   (on-deletes [_ deletions]  (swap! indeces rhs-deletes i keys deletions) nil)))
+   (on-upserts [_ upsertions] (swap! indeces rhs-upserts i keys upsertions join-fn) nil)
+   (on-deletes [_ deletions]  (swap! indeces rhs-deletes i keys deletions join-fn) nil)))
 
 ;;; attach lhs to all rhses such that any change to an rhs initiates an
 ;;; attempt to [re]join it to the lhs and vice versa.

;;--------------------------------------------------------------------------------
;; impl

;; TODO: return a pair - first is notification fn, second is new-indeces - use swap*! to apply

(defn- derive-joins [v rhs-indeces ks join-fn]

(deftest test-join-model
  (let [as (versioned-optimistic-map-model (str :as) :name :version >)
	bs (versioned-optimistic-map-model (str :bs) :name :version >)
	cs (versioned-optimistic-map-model (str :cs) :name :version >)
	join (join-model "join-model" [[as :fk-b :fk-c][bs :b][cs :c]] join-abc)
	view (test-view "test")
	a1 (->A :a1 0 :b :c)
	a2 (->A :a2 0 :b :c)
	b1 (->B :b1 0 :b "b1-data")
	b2 (->B :b2 0 :b "b2-data")
	c1 (->C :c1 0 :c "c1-data")
	c2 (->C :c2 0 :c "c2-data")]
    (is (= [[{}{}]{}{}] (data join)))

    (attach join view)
    (is (= nil (data view)))

    ;; initial data
    (on-upsert bs b1)
    (is (= [[{}{}]{:b [b1]} {}] (data join)))

    (on-upsert cs c1)
    (is (= [[{}{}]{:b [b1]} {:c [c1]}] (data join)))

    ;; join - lhs - a1
    (on-upsert as a1)
    (is (= [[{:b [a1]}{:c [a1]}]{:b [b1]} {:c [c1]}] (data join)))

    ;; join - new rhs - b2
    (on-upsert bs b2)
    (is (= [[{:b [a1]}{:c [a1]}]{:b [b1 b2]} {:c [c1]}] (data join)))

    ;; join - new rhs - c2
    (on-upsert cs c2)
    (is (= [[{:b [a1]}{:c [a1]}]{:b [b1 b2]} {:c [c1 c2]}] (data join)))

    ;; join - new lhs - a2
    (on-upsert as a2)
    (is (= [[{:b [a1 a2]}{:c [a1 a2]}]{:b [b1 b2]} {:c [c1 c2]}] (data join)))

    ;; what about amend-in/out ?
    ;; what about delete ?

    ))
