2013-09-28 15 views
13

Sono interessato alle entità e ai relativi timestamp. In sostanza, voglio un elenco di entità ordinato nel tempo.Recupera l'entità più recente da Datomic

A tal fine, ho composto le seguenti funzioni:

(defn return-posts 
    "grabs all posts from Datomic" 
    [] 
    (d/q '[:find ?title ?body ?slug 
     :where 
     [?e :post/title ?title] 
     [?e :post/slug ?slug] 
     [?e :post/body ?body]] (d/db connection))) 

(defn get-postid-from-slug 
    [slug] 
    (d/q '[:find ?e 
     :in $ ?slug 
     :where [?e :post/slug ?slug]] (d/db connection) slug)) 

(defn get-post-timestamp 
    "given an entid, returns the most recent timestamp" 
    [entid] 
    (-> 
    (d/q '[:find ?ts 
      :in $ ?e 
      :where 
      [?e _ _ _] 
      [?e :db/txInstant ?ts]] (d/db connection) entid) 
    (sort) 
    (reverse) 
    (first))) 

cui mi sento deve essere un hack radicata nell'ignoranza.

Qualcuno più esperto nell'uso idiomatico di Datomic inserirà e aggiornerà i miei paradigmi?

risposta

7

mi dava fastidio l'idea di aggiungere timestamp supplementari a un database che comprende nominalmente tempo come principio di prima classe e quindi (dopo una notte di rimuginare sugli approcci delineati da Ulrik Sandberg) evoluto la seguente funzione:

(defn return-posts 
    "grabs all posts from Datomic" 
    [uri] 
    (d/q '[:find ?title ?body ?slug ?ts 
     :where 
     [?e :post/title ?title ?tx] 
     [?e :post/slug ?slug] 
     [?e :post/body ?body] 
     [?tx :db/txInstant ?ts]] (d/db (d/connect uri)))) 

E 'idiomatica in Datalog di omettere il legame con l'ID della transazione stessa come noi di solito non ci interessa. In questa situazione, ci interessa molto e, nelle parole di August Lileaas, desideriamo "attraversare la transazione" (ci sono situazioni in cui vorremmo il tempo di creazione del post, ma per questa applicazione il tempo di transazione sarà sufficiente per ordinare le entità).

Uno svantaggio notevole di questo approccio è che le voci modificate di recente verranno inserite nell'elenco. A tal fine, dovrò fare qualcosa in seguito per ottenere la loro "prima apparizione" in Datomic per la cronologia degli articoli standard dei blog.

Per riassumere: Ho associato l'ID dell'entità della transazione per l'ID dell'entità "post", quindi ho cercato il timestamp della transazione con questa funzione per l'ordinamento successivo.

+2

Vale la pena notare che, AFAIK, questo fornisce al momento il valore corrente di: post/titolo è stato asserito per l'entità e non quando l'entità è stata l'ultima oggetto di una transazione. – spieden

2

Non c'è davvero un modo più elegante per fare questo che attraversare le transazioni. Questo è il motivo per cui preferisco avere un attributo specifico di dominio separato per i timestamp, invece di fare affidamento sui timestamp della transazione da Datomic. Un esempio in cui ciò è necessario è la fusione: diciamo che hai un wiki e vuoi unire due pagine wiki. In tal caso, probabilmente si desidera controllare da sé il timestamp e non utilizzare il timestamp della transazione.

Mi piace avere gli attributi :created-at e :changed-at. Quando ho TRANSACT nuove entità:

[[:db/add tempid :post/slug "..."] 
[:db/add tempid :post/title "A title"] 
[:db/add tempid :created-at (java.util.Date.)] 
[:db/add tempid :changed-at (java.util.Date.)]] 

Poi per gli aggiornamenti:

[[:db/add post-eid :post/title "An updated title"] 
[:db/add post-eid :changed-at (java.util.Date.)]] 

In questo modo tutto quello che devo fare è leggere il: creato-at attributo del soggetto, che sarà pronta e aspettando nell'indice.

(defmacro find-one-entity 
    "Returns entity when query matches, otherwise nil" 
    [q db & args] 
    `(when-let [eid# (ffirst (d/q ~q ~db [email protected]))] 
    (d/entity ~db eid#))) 

(defn find-post-by-slug 
    [db slug] 
    (find-one-entity 
    '[:find ?e 
     :in $ ?slug 
     :where 
     [?e :post/slug ?slug]] 
    db 
    slug)) 

;; Get timestamp 
(:created-at (find-post-by-slug db "my-post-slug"))