2015-02-25 5 views
3

Я попытался использовать непосредственно hashmap Clojure с MapDB и столкнулся с странным поведением. Я проверил источники Clojure и MapDB и не смог понять проблему.Использование структуры данных Clojure с помощью MapDB

Сначала все выглядит отлично:

lein try org.mapdb/mapdb "1.0.6" 

; defining a db for the first time 
(import [org.mapdb DB DBMaker]) 
(defonce db (-> (DBMaker/newFileDB (java.io.File. "/tmp/mapdb")) 
       .closeOnJvmShutdown 
       .compressionEnable 
       .make)) 
(defonce fruits (.getTreeMap db "fruits-store")) 
(do (.put fruits :banana {:qty 2}) (.commit db)) 

(get fruits :banana) 
=> {:qty 2} 
(:qty (get fruits :banana)) 
=> 2 
(first (keys (get fruits :banana))) 
=> :qty 
(= :qty (first (keys (get fruits :banana)))) 
=> true 

CTRL-D 
=> Bye for now! 

Тогда я пытаюсь получить доступ к данным снова:

lein try org.mapdb/mapdb "1.0.6" 

; loading previsously created db 
(import [org.mapdb DB DBMaker]) 
(defonce db (-> (DBMaker/newFileDB (java.io.File. "/tmp/mapdb")) 
       .closeOnJvmShutdown 
       .compressionEnable 
       .make)) 
(defonce fruits (.getTreeMap db "fruits-store")) 

(get fruits :banana) 
=> {:qty 2} 
(:qty (get fruits :banana)) 
=> nil 
(first (keys (get fruits :banana))) 
=> :qty 
(= :qty (first (keys (get fruits :banana)))) 
=> false 
(class (first (keys (get fruits :banana)))) 
=> clojure.lang.Keyword 

Каким же самое ключевое слово отличается относительно =? Есть ли какая-то странная проблема с эталоном?

+0

Похоже, что что-то не так с mapdb. '(def k? (first (keys (get fruits: banana))))' '(= k? (ключевое слово (name k?)))' => 'false' – noisesmith

+0

Взяв другой взгляд - mapdb специально не сделан для поддержки типов данных Clojure. Я вовсе не удивлен тем, что родные родовые инструменты сериализации Java не действительно правильно используют ключевые слова Clojure. Я думаю, что это похоже на классическую проблему с serializing booleans и false, которые не работают должным образом. Вы можете захотеть clj-mapdb (хотя он все еще находится в бета-версии API). – noisesmith

ответ

2

Проблема связана с тем, как работает равенство ключевых слов. Рассматривая реализацию = function, мы видим, что, поскольку ключевые слова не являются clojure.lang.Number или clojure.lang.IPersistentCollection, их равенство равно , определяемому с точки зрения метода Object.equals. Снимая source of clojure.lang.Keyword, мы узнаем, что ключевые слова не переопределяют Object.equals и поэтому два ключевых слова равны, если они те же объект.

Сериализатор по умолчанию MapDB по умолчанию - org.mapdb.SerializerPojo, подкласс org.mapdb.SerializerBase. В its documentation мы можем прочитать, что это

Serializer, который использует «байт заголовка» для сериализации/десериализации большинство классов от «java.lang» и «java.util» пакетов.

К сожалению, это не так хорошо с классами clojure.lang; Он не сохраняет личность ключевых слов, тем самым нарушая равенство. Чтобы исправить это, давайте попробуем написать наш собственный serializer, используя EDN format. Альтернативно, вы можете рассмотреть, скажем, Nippy, и использовать в нашем MapDB.

(require '[clojure.edn :as edn]) 

(deftype EDNSeralizer [] 
    ;; See docs of org.mapdb.Serializer for semantics. 
    org.mapdb.Serializer 
    (fixedSize [_] 
    -1) 
    (serialize [_ out obj] 
    (.writeUTF out (pr-str obj))) 
    (deserialize [_ in available] 
    (edn/read-string (.readUTF in))) 
    ;; MapDB expects serializers to be serializable. 
    java.io.Serializable) 

(def edn-serializer (EDNSeralizer.)) 

(import [org.mapdb DB DBMaker]) 
(def db (.. (DBMaker/newFileDB (java.io.File. "/tmp/mapdb")) 
      closeOnJvmShutdown 
      compressionEnable 
      make)) 

(def more-fruits (.. db 
        (createTreeMap "more-fruits") 
        (valueSerializer (EDNSeralizer.)) 
        (makeOrGet))) 
(.put more-fruits :banana {:qty 2}) 
(.commit db) 

После того как карта more-fruits дерева возобновлена ​​в JVM с определенным EDNSeralizer :qty объекта хранится внутри будет тот же объекта, как и любого другого :qty экземпляра. В результате проверки корректности будут корректно работать.

+0

Спасибо за ссылки на источники и объяснения. Это забавно, хотя 'clojure.lang.Keyword' реализует' Comparable', что делает это правильным: '(.compareTo: qty (first (keys (get fruits: banana))))' – lerouxrgd

+0

Я не совсем понимаю, как 'edn-serializer' var используется API MapDB. Не могли бы вы экстраполировать на это немного больше? – mlb