تابع dissoc-in و ساخت in-memory database در کلوژر

این مطلب، نه آموزشه نه سوال. یه کم دلم میخواد غر بزنم :grinning:

تو این مدتی که با کلوژر کار میکنم، شاید تنها مشکلی که بهش برخورد کردم نبود تابعی به نام dissoc-in باشه.
زبانی که تا این حد روی data-oriented بودن تاکید داره، کاش قابلیتهای بیشتری برای درست کردن in-memory database داشت.
منطقا وقتی assoc و dissoc داریم و همچنین assoc-in داریم، انتظار میره dissoc-in هم داشته باشیم :neutral_face:

برای همچین کاری (ساخت یه database ساده با atom و hash-map)، شما چه راهی رو پیشنهاد میکنید؟
من یه اتم درست میکنم و با ۲تا تابع توش insert و delete انجام میدم:

(ns myapp.auth                                                                                                                                                                                                                                                                                                 
  (:require [clj-time [core :as t]
                      [coerce :as c]]))

(def a (atom {}))

(defn dissoc-in
  [m [k & ks :as keys]]
  (if ks
    (if-let [nextmap (get m k)]
      (let [newmap (dissoc-in nextmap ks)]
        (if (seq newmap)
          (assoc m k newmap)
          (dissoc m k)))
      m)
    (dissoc m k)))

(defn insert-a [table k v]
  (swap! a assoc-in [table k] v))

(defn delete-a [table k]
  (swap! a dissoc-in [table k]))

(insert-a :auth "5592e104-9451-44fd-bb2e-e8c807e6add9" {:email     "[email protected]"
                                                        :timestamp (c/to-long (t/now))
                                                        :role      [:user :admin]})

(insert-a :email "[email protected]" {:auth      "5592e104-9451-44fd-bb2e-e8c807e6add9"
                                   :timestamp (c/to-long (t/now))})

(insert-a :auth "ec5f0a92-699a-4c12-b42f-84f383aabd21" {:email     "[email protected]"
                                                        :timestamp (c/to-long (t/now))
                                                        :role      [:user :admin]})

(delete-a :auth "5592e104-9451-44fd-bb2e-e8c807e6add9")
(delete-a :auth "ec5f0a92-699a-4c12-b42f-84f383aabd21")

خروجی نهایی هم چیزی شبیه این میشه (اگه توابع delete-a اجرا نشن):

{:auth {"5592e104-9451-44fd-bb2e-e8c807e6add9"{:email "[email protected]",
                                               :timestamp 1566605134386,
                                               :role [:user :admin]},
        "ec5f0a92-699a-4c12-b42f-84f383aabd21" {:email "[email protected]",
                                                :timestamp 1566605137291,
                                                :role [:user :admin]}},
 :email {"[email protected]" {:auth "5592e104-9451-44fd-bb2e-e8c807e6add9",
                           :timestamp 1566605135707}}}

دیتابیس ساده و atomic که با یه filter ساده میشه ازش کوئری گرفت.
فقط کاش درست کردنش یه کم هموارتر بود. حداقل اینکه لازم نبود تابع dissoc-in رو از stackoverflow کپی کنم :sweat_smile:

با کلوژر کار نکردم اما اگه ConcurrentHashMap جاوا رو در کلوژر استفاده کنید خوبه

نمونه کد در سکالا

trait KVStorage[K,V] {
  def get(k: K): Option[V]
  
  def put(k:K, v:V): Unit

}
import java.util.concurrent.ConcurrentHashMap

object InMemory {
  def apply[K,V] = new KVStorage[K,V] {
    val store = new ConcurrentHashMap[K,V]()
    def get(k: K): Option[V] = Option(store.get(k))
    
    def put(k:K, v:V): Unit = store.put(k,v)

  }

}

val db = InMemory[String,String]

db.put("key", "value")

println(db.get("key"))

1 Like

متشکرم.
ترجیهم اینه که از دیتاتایپهای جاوا استفاده نکنم و خب مسلما میخوام کدم ساده باشه (java interop یه کم پیچیده میکنه قضیه رو) ولی اینم اینطور که میگن atomic هست و از اسمش پیداست برای concurrency بهینه شده. (امیدوارم از lock استفاده نکرده باشن)

بعدا حتما یه نگاهی بهش میندازم.

1 Like

بله این اصلا lock نداره و در concurrency بالا عالی عمل میکنه

در داده های خود clojure هم به کار رفته

https://clojuredocs.org/clojure.core/enumeration-seq

1 Like

dissoc-in وجود نداره اما می تونی از update-in استفاده کنی. این جوری:

user=> (update-in {:a {:b {:x 3} :c 1}} [:a :b] dissoc :x)
{:a {:b {}, :c 1}}
user=>

در مورد in-memory دیتابیس هم اگر چیزی ساده ای می خوای بد نیست یه datascript یه نگاه بندازی

1 Like

ممنون بابت قطعه کد.

بله datascript رو دیده بودم و اینطور که میگن توی دیتاهای کوچیک خیلی سریعه و توی دیتاهای بزرگ و پیچیده هم بهتر از هیچیه.
ولی زیاد خوشم نیومد ازش. به نسبت کاری که میکنه زیادی پیچیده و abstract درستش کردن. (شایدم من زود قضاوت کردم)