         distance (second result)]
     [(nth item-names index) distance]))
 
+(defn document-to-vector
+  "TODO"
+  [all-terms idf document]
+  (->> document (to-terms) (tf-idf-document all-terms idf) (m/sparse-array)))
+
 (defn find-knn
   "TODO"
   [tf-idf-data query-document]
   (let [{:keys [idf all-terms item-names data]} tf-idf-data
-        query-vector (->> query-document
-                          (to-terms)
-                          (tf-idf-document all-terms idf)
-                          (m/sparse-array))
+        query-vector (document-to-vector all-terms idf query-document)
         nearest-neighbors (knn data
                                query-vector
                                :distance-fn cosine-similarity)]

  (with-open [file-reader (-> file-path
                              gzip-reader
                              PushbackReader.)]
    (let [tf-idf-stream (read-edn-stream file-reader)]
      {:idf (nth tf-idf-stream 0)
       :all-terms (nth tf-idf-stream 1)
       :item-names (nth tf-idf-stream 2)
       :data (m/sparse-matrix (nthrest tf-idf-stream 3))})))

(defn knn-named-result
  "TODO"

  (:import java.io.PushbackReader))

(defn read-edn-stream
  "Return a lazy sequence of (transform-fn item) for each item parsed from a
  stream of edn objects. transform-fn defaults to identity. reader must be a
  java.io.PushbackReader or some derivee. Since it is lazy, the stream must
  remain open while new items are being requested."
  ([reader] (read-edn-stream reader identity))
  ([reader transform-fn]
    (lazy-seq
      (when-let [line (edn/read {:eof nil} reader)]
        (cons (transform-fn line) (read-edn-stream reader transform-fn))))))

(defn read-tf-idf-data
  "TODO"
