2016-05-13 56 views
8

設置考慮膜和流延的下面DataScript數據庫,與來自learndatalogtoday.org竊取的數據:以下代碼可以在JVM/Clojure的REPL或ClojureScript REPL只要project.clj包含[datascript "0.15.0"]作爲依賴執行。如何構建一個完全匹配DataScript中的引用向量的查詢?

(ns user 
    (:require [datascript.core :as d])) 

(def data 
    [["First Blood" ["Sylvester Stallone" "Brian Dennehy" "Richard Crenna"]] 
    ["Terminator 2: Judgment Day" ["Linda Hamilton" "Arnold Schwarzenegger" "Edward Furlong" "Robert Patrick"]] 
    ["The Terminator" ["Arnold Schwarzenegger" "Linda Hamilton" "Michael Biehn"]] 
    ["Rambo III" ["Richard Crenna" "Sylvester Stallone" "Marc de Jonge"]] 
    ["Predator 2" ["Gary Busey" "Danny Glover" "Ruben Blades"]] 
    ["Lethal Weapon" ["Gary Busey" "Mel Gibson" "Danny Glover"]] 
    ["Lethal Weapon 2" ["Mel Gibson" "Joe Pesci" "Danny Glover"]] 
    ["Lethal Weapon 3" ["Joe Pesci" "Danny Glover" "Mel Gibson"]] 
    ["Alien" ["Tom Skerritt" "Veronica Cartwright" "Sigourney Weaver"]] 
    ["Aliens" ["Carrie Henn" "Sigourney Weaver" "Michael Biehn"]] 
    ["Die Hard" ["Alan Rickman" "Bruce Willis" "Alexander Godunov"]] 
    ["Rambo: First Blood Part II" ["Richard Crenna" "Sylvester Stallone" "Charles Napier"]] 
    ["Commando" ["Arnold Schwarzenegger" "Alyssa Milano" "Rae Dawn Chong"]] 
    ["Mad Max 2" ["Bruce Spence" "Mel Gibson" "Michael Preston"]] 
    ["Mad Max" ["Joanne Samuel" "Steve Bisley" "Mel Gibson"]] 
    ["RoboCop" ["Nancy Allen" "Peter Weller" "Ronny Cox"]] 
    ["Braveheart" ["Sophie Marceau" "Mel Gibson"]] 
    ["Mad Max Beyond Thunderdome" ["Mel Gibson" "Tina Turner"]] 
    ["Predator" ["Carl Weathers" "Elpidia Carrillo" "Arnold Schwarzenegger"]] 
    ["Terminator 3: Rise of the Machines" ["Nick Stahl" "Arnold Schwarzenegger" "Claire Danes"]]]) 

(def conn (d/create-conn {:film/cast {:db/valueType :db.type/ref 
             :db/cardinality :db.cardinality/many} 
          :film/name {:db/unique :db.unique/identity 
             :db/cardinality :db.cardinality/one} 
          :actor/name {:db/unique :db.unique/identity 
             :db/cardinality :db.cardinality/one}})) 
(def all-datoms (mapcat (fn [[film actors]] 
          (into [{:film/name film}] 
           (map #(hash-map :actor/name %) actors))) 
         data)) 
(def all-relations (mapv (fn [[film actors]] 
          {:db/id [:film/name film] 
          :film/cast (mapv #(vector :actor/name %) actors)}) data)) 

(d/transact! conn all-datoms) 
(d/transact! conn all-relations) 

描述概括地說,有兩種類型的實體在該數據庫中的膜和演員(字旨在被ungendered) - 和3種datoms的:

  • 膜實體: :film/name(唯一的字符串)
  • 膜實體::film/cast(多參考文獻)
  • 演員實體::actor/name(唯一的字符串)

問題我想構造一個問一個查詢:該薄膜具有這些N演員,這些N演員單獨,出現了作爲唯一的明星,對於N> = 2?

例如,RoboCop出演的Nancy Allen,Peter Weller,Ronny Cox,但是沒有這部電影僅僅是艾倫和韋勒的前兩部。因此,我希望下面的查詢產生空集:

(d/q '[:find ?film-name 
     :where 
     [?film :film/name ?film-name] 
     [?film :film/cast ?actor-1] 
     [?film :film/cast ?actor-2] 
     [?actor-1 :actor/name "Nancy Allen"] 
     [?actor-2 :actor/name "Peter Weller"]] 
    @conn) 
; => #{["RoboCop"]} 

但是,查詢是有缺陷的,因爲我不知道該如何表達,任何比賽應該排除誰不阿倫或Weller-任何演員再次,我想找到只有艾倫和韋勒沒有任何其他演員合作的電影,所以我想調整上述查詢來產生空集。我如何調整此查詢以執行此要求?

回答

2

由於DataScript沒有否定(截至2016年5月),我不相信在'純'Datalog中有一個靜態查詢是可能的。

我去的辦法是:

  1. 構建查詢程序添加一個聲明,中投必須包含N個演員的N個條款
  2. 加入其中,由於電影中的謂語功能,數據庫和actor的集合,使用EAVT索引來查找每部電影是否有不在集合中的actor。

這是一個基本的實現

(defn only-those-actors? [db movie actors] 
    (->> (datoms db :eavt movie :film/cast) seq 
    (every? (fn [[_ _ actor]] 
       (contains? actors actor))) 
    )) 

(defn find-movies-with-exact-cast [db actors-names] 
    (let [actors (set (d/q '[:find [?actor ...] :in $ [?name ...] ?only-those-actors :where 
          [?actor :actor/name ?name]] 
         db actors-names)) 
     query {:find '[[?movie ...]] 
       :in '[$ ?actors ?db] 
       :where 
       (concat 
       (for [actor actors] 
        ['?movie :film/cast actor]) 
       [['(only-those-actors? ?db ?movie ?actors)]])}] 
    (d/q query db actors db only-those-actors?))) 
+0

對不起,延遲了(抱歉讓你煩惱)!在查詢中加入像「只有那些演員」這樣的檢查有什麼好處嗎?與通過https://gist.github.com/fasiha/647a48420770536a4fa952a4b38f69d7#file-stackoverflow-clj-L97-L116獲取查詢外的所有匹配和後處理相比,這個實現是否會因爲不去'datoms'路由而受到影響? –

+0

你的實現看起來也不錯!你巧妙地使用獨特的。作爲一種優化,也許你可以只計算不同角色的數量,這比排序更有效。關於是否使用datoms:我不會對性能做任何聲明,你應該運行基準測試。 –

+0

我不得不在代碼中清理一個或兩個額外的東西,因爲它是目前:https://gist.github.com/fasiha/647a48420770536a4fa952a4b38f69d7#file-stackoverflow-clj-L60-L85(如果你想更新答案),但它的作品!謝謝!! –

0

您可以通過一個實體的:film/cast領域使用謂詞的樂趣和d/entity一起過濾datoms。直到Datascript不支持否定(而不是運算符等),這種方法看起來更直截了當。

看行(= a (:age (d/entity db e))中的Datascript here

[{:db/id 1 :name "Ivan" :age 10} 
{:db/id 2 :name "Ivan" :age 20} 
{:db/id 3 :name "Oleg" :age 10} 
{:db/id 4 :name "Oleg" :age 20}] 

... 

(let [pred (fn [db e a] 
      (= a (:age (d/entity db e))))] 
    (is (= (q/q '[:find ?e 
       :in $ ?pred 
       :where [?e :age ?a] 
         [(?pred $ ?e 10)]] 
       db pred) 
     #{[1] [3]}))))) 

在你的情況下,測試的情況下,謂詞的身體可能是這個樣子

(clojure.set/subset? actors (:film/cast (d/entity db e)) 

在問候的表現,d/entity調用速度很快,因爲它是按索引查找的。

相關問題