【问题标题】:Full-text search across several attributes in Datomic在 Datomic 中跨多个属性进行全文搜索
【发布时间】:2018-03-04 20:25:26
【问题描述】:

我有一个看起来差不多的模型(在 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]))])

我将如何实现这个?

【问题讨论】:

    标签: clojure datomic


    【解决方案1】:

    我们遇到了同样的情况,最终使用了一种解决方法:

    我们创建了一个连接所有其他字符串属性的属性。当然,在该属性上使用全文查询。

    【讨论】:

    • 这不是要求澄清,而是建议解决。这不是“不是答案”。
    • 这是一个聪明的主意,虽然我会小心在数据库中创建太大的字符串值,因为这会影响 Datomic 索引和存储系统的其他部分
    【解决方案2】:

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

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

    【讨论】:

    • 关于 Lucence 索引作业的一致性的好点 - 我没有意识到这一点。
    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 2023-04-04
      • 1970-01-01
      • 2012-02-19
      • 2019-10-22
      • 2010-11-10
      • 2020-08-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多