clojure - 如何构造与DataScript中的引用向量完全匹配的查询?

标签 clojure clojurescript datomic datalog datascript

设置考虑以下电影和类型转换的DataScript数据库,以及从learndatalogtoday.org中窃取的数据:只要project.clj包含[datascript "0.15.0"]作为依赖项,就可以在JVM/Clojure REPL或ClojureScript REPL中执行以下代码。

(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)

说明概括地说,该数据库中有两种实体-电影和 Actor (打算不使用文字的单词)-三种硅藻:
  • 电影实体::film/name(唯一字符串)
  • 电影实体::film/cast(多个引用)
  • Actor 实体::actor/name(唯一字符串)

  • 问题我想构造一个查询,该问题是:对于N> = 2,哪些电影具有这些N Actor ,而仅这些N Actor 是唯一的明星?

    例如,RoboCop由Nancy Allen,Peter Weller,Ronny Cox主演,但没有一部电影仅由Allen和Weller主演。因此,我希望以下查询产生空集:
    (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"]}
    

    但是,该查询存在缺陷,因为我不知道该如何表达任何比赛都应排除非艾伦或韦勒的 Actor -同样,我想找到只有艾伦和韦勒没有其他 Actor 合作的电影,因此我想调整上面的查询以产生空集。如何调整此查询以强制执行此要求?

    最佳答案

    因为DataScript没有取反(截至2016年5月),所以我认为使用“纯” Datalog中的一个静态查询是不可能的。

    我的方法是:

  • 以编程方式构建查询,以添加N个子句,该子句声明强制类型转换必须包含N个参与者
  • 添加一个谓词函数,在给定电影,数据库和 Actor ID集合的情况下,使用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?)))
    

    关于clojure - 如何构造与DataScript中的引用向量完全匹配的查询?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37200086/

    相关文章:

    java - 从 Clojure 调用带有可选参数的 java 函数

    javascript - 将 javascript 翻译为 Clojurescript

    browser - 启动 ClojureScript 浏览器 REPL

    clojure - 如何帮助Clojure理解0是最小的自然数?

    Spring bean 初始化 - Clojure

    java - Clojure let 与多元性

    time - 将早于上一次交易的数据导入到 datomic 中

    many-to-many - datomic 中的限定关系

    dictionary - doseq 中的 assoc 字符串导致空映射

    clojure - 枚举命名空间并在 ClojureScript 中动态加载它们