 	  (log2 :info (str " rhs: " model ", " i ", " key))
 	  (swap! indeces conj {})	; add index for this model
 	  ;; view rhs model
-	  (attach model (rhs-view indeces i key))))
+	  (attach model (rhs-view indeces i key lhs-keys join-fn))))
     indeces))
 
 (deftype JoinModel [^String name ^Atom state ^Atom views]

;;--------------------------------------------------------------------------------
;; tests

(defrecord D [name ^int version e]
  Object
  (^String toString [_] (str name)))

(defrecord B [name ^int version c]
  Object
  (^String toString [_] (str name)))

(defrecord A [name ^int version ^B b ^D d]
  Object
  (^String toString [_] (str name)))

(defrecord ACE [a ^int version c e]
  Object
  (^String toString [_] (str a)))

;; an example of an aggressive join-fn
;; a "lazy" join fn would define the same interface, but hold references to the A,B and C...
;; a really clever impl might start lazy and become agressive during serialisation..
(defn- ^ACE join-ace [^A a ^B b ^D d]
  (->ACE (:name a) (+ (:version a) (:version b) (:version d)) (:c b) (:e d)))

(deftest test-join-ace
  (is (= (->ACE :a 0 :c :e) 
	 (join-ace (->A :a 0 :b :d)(->B :b 0 :c)(->D :d 0 :e)))))

;;; calculate all permutations for a given list of sets...
;;; e.g. 
;;; (permute [[]] [[:a1 :a2][:b1 :b2 :b3][:c1]]) ->
;;; ([:a1 :b1 :c1] [:a1 :b2 :c1] [:a1 :b3 :c1] [:a2 :b1 :c1] [:a2 :b2 :c1] [:a2 :b3 :c1])

;;; TODO: should use recurse
(defn- permute [state [head & tail]]
  (if (empty? head)
    state
    (permute (mapcat (fn [i] (map (fn [j] (conj i j)) head)) state) tail)))

;;; consider outer joins
;; (defn- permute2 [inner state [head & tail]]
;;   (if (empty? tail)
;;     state
;;     (if-let [head (if (and (empty? head) inner) nil [nil])]
;; 	(permute (mapcat (fn [i] (map (fn [j] (conj i j)) head)) state) tail))))

(deftest test-permute
  (is (= 

(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 ?

    ))
