在Datomic中跨多个属性进行全文搜索

时间:2018-03-04 20:25:26

标签: clojure datomic

我有一个看起来大致相似的模型(在JSON中):

{"gender": "female", 
 "name": [
  {"family": "Smith", 
   "given": ["Samantha"], 
   "middle": ["Lee"]]}}

大约有6M记录具有这样的结构。我需要使用OR子句提供人名的所有组件的全文搜索。例如。如果用户输入" smith",我需要检查所有给定,中间和姓氏。

在Datomic,我做了一个架构:

   {:db/ident       :model/name
    :db/valueType   :db.type/ref
    :db/isComponent true
    :db/cardinality :db.cardinality/many}

   {:db/ident       :model.name/family
    :db/valueType   :db.type/string
    :db/cardinality :db.cardinality/one
    :db/fulltext    true}

   {:db/ident       :model.name/given
    :db/valueType   :db.type/string
    :db/cardinality :db.cardinality/many
    :db/fulltext    true}

   {:db/ident       :model.name/middle
    :db/valueType   :db.type/string
    :db/cardinality :db.cardinality/many
    :db/fulltext    true}          

请注意,我为这些属性提供了全文索引。现在,当我通过单个属性查询时,例如family,性能很好(约100毫秒):

(def query-all
  '[:find [(rand 100 ?model) ...]
    :in $ ?search
    :where
    [(fulltext $ :model.name/family ?search) [[?name _ _ _]]]
    [?model :model/name ?name]])

但是当我使用OR子句添加其他条件时,性能会急剧下降(20秒):

(def query-all
  '[:find [(rand 100 ?model) ...]
    :in $ ?search
    :where
    (or
     [(fulltext $ :model.name/family ?search) [[?name _ _ _]]]
     [(fulltext $ :model.name/given ?search) [[?name _ _ _]]]
     [(fulltext $ :model.name/middle ?search) [[?name _ _ _]]])
    [?model :model/name ?name]])

我的问题是,我该如何改进?

如果我们更进一步,那么也不仅仅是通过名称,而是通过地址的组件找到它也会很棒。理想情况下,会有以下查询(也很慢):

(def query-all
  '[:find [(rand 100 ?model) ...]
    :in $ ?search
    :where
    (or

     (and
      [(fulltext $ :model.name/given ?search) [[?e _ _ _]]]
      [?p :model/name ?e])

     (and
      [(fulltext $ :model.name/middle ?search) [[?e _ _ _]]]
      [?p :model/name ?e])

     (and
      [(fulltext $ :model.name/prefix ?search) [[?e _ _ _]]]
      [?p :model/name ?e])

     (and
      [(fulltext $ :model.name/suffix ?search) [[?e _ _ _]]]
      [?p :model/name ?e])

     (and
      [(fulltext $ :model.name/family ?search) [[?e _ _ _]]]
      [?p :model/name ?e])

     (and
      [(fulltext $ :model.address/city ?search) [[?e _ _ _]]]
      [?p :model/address ?e])

     (and
      [(fulltext $ :model.address/state ?search) [[?e _ _ _]]]
      [?p :model/address ?e]))])

我该如何实现?

3 个答案:

答案 0 :(得分:2)

We were in the same situation and ended up using a work around:

We created an attribute that concats all of the other string attributes. Querying using fulltext over that attribute, of course.

答案 1 :(得分:1)

我认为您不应该使用or,而是对db执行四个不同的查询,并将这些查询的结果连接起来。数据组规则功能很有用,但它们在实现结果方面往往会爆炸。但/ / p>

请记住,在db处 - 指针是不可变的,并且会为要求它的多个查询提供一致的结果。对于全文搜索总是如此,因为Lucene索引作业在事务之后运行,但对于大多数应用程序而言,这可能不会那么重要。

答案 2 :(得分:0)

遇到同样的情况,我们的(or)加入全文搜索的时间为30-40秒。最终用一些java本机字符串包含的调用替换,将尝试该线程中的其他一些建议。