【问题标题】:Query for ANY value in complex key (AKA wildcard)查询复杂键中的任何值(AKA 通配符)
【发布时间】:2018-08-27 18:47:07
【问题描述】:

我浏览了文档和 SO 问题,但没有为我的特定案例找到任何明确的答案。

查看这个复杂的(发射数组)键映射函数

function(doc) {
    if (doc.userFirstName && doc.userLastName && doc.userGender && doc.homeCountry && doc.homeCity) {
      emit([
        doc.userFirstName,
        doc.userMiddleName,
        doc.userLastName,            
      ], null)
    }

我想用其中一些为空的值来查询它 - 因此查询可以接受来自键的 ANY 值,即

userFirstName = *anyvalue*
userMiddleName = *anyvalue*
userLastName = "Mozart"

如果可能 - 我的 startKey= 和 endKey= 请求参数应该是什么样的? 我试过了

startkey=[{},{},"Mozart"]&endkey=[{},{},"Mozart"]

但无济于事 - 没有行..

【问题讨论】:

  • 这对于单个索引是不可能的。您只能对键的第一部分进行子键搜索。
  • “密钥的第一部分”是什么意思?当我们搜索 lastName 时,查询中应该存在(使用呈现的映射函数)firstName 和 middleName 吗?
  • 我不确定“密钥的第一部分”是如何令人困惑的。您发出的密钥是[doc.userFirstName, doc.userMiddleName, doc.userLastName]。 “第一部分”将是在其他部分之前的任何部分。例如,名字或名字的第一部分。例如,给定名称“John B Smith”,键可能是["John","B","Smith"],然后您可以搜索["J"]、或["John",B"]["John","B","S"],但不能搜索[{},"B",{}][{},{},"Smith"]
  • 谢谢你,我猜对了。

标签: couchdb


【解决方案1】:

这对于单个索引是不可能的。您只能对密钥的第一部分进行子密钥搜索。为了满足您的需求,您将需要多个索引 - 根据您需要支持的查询的复杂程度,您可能需要大量索引。

为了支持按名字搜索,您的示例中的索引很好。

为了支持按姓氏搜索,您可能会反转索引 - 或索引姓氏。

如果您需要按中间名搜索,则需要第三个索引。如果您想按名字和姓氏搜索(省略中间名),则需要另一个索引等。

【讨论】:

    【解决方案2】:

    couchdb 查询中的“Any_value”通配符是不可能的(但是,向开发人员提问 - 为什么不呢?),因此使用空 (=accept_any) 值执行多维查找的唯一方法是维护所有可能的索引非空搜索值的组合。我最终得到了这个 _design 文档:

    let ddoc = {
          '_id': '_design/search',
          'views': {
            'firstOnly': {'map': firstOnly},
            'middleOnly': {'map': middleOnly},
            'lastOnly': {'map': lastOnly},
            'firstLast': {'map': firstLast},
            'firstMiddle': {'map': firstMiddle},
            'middleLast': {'map': middleLast}
          }
        };
    

    其中(即已知的中间名和姓氏)映射函数如下所示:

    const middleLast =
      `function(doc) {
        if (doc.userMiddleName && doc.userLastName) {
          emit([
            doc.userMiddleName,
            doc.userLastName,
          ], null)
        }
      }`;
    

    那么我可以根据已知的数据集查询相应的视图吗。

    所以六个复杂的关键索引而不是一个。相当繁重(如果有数百万条记录),但不幸的是,这是唯一可能的解决方案。

    感谢所有响应者,您的帮助是无价的。

    【讨论】:

      【解决方案3】:

      您可以为每个文档发出多个索引条目。为了解决您的简单示例,您可以创建如下索引:

      function(doc) {
          if (doc.userFirstName && doc.userLastName && doc.userGender && doc.homeCountry && doc.homeCity) {
            emit(doc.userFirstName, null);
            emit(doc.userMiddleName, null);
            emit(doc.userLastName, null);
          }
      

      它会通过简单的索引搜索找到“Mozart”。我怀疑您的实际要求更复杂 - 例如,名字或中间名也可能是“Mozart”,因此如果您的搜索专门针对 Lastname="Mozart",则会返回误报。

      【讨论】:

      • 我是这么想的,但你是对的——这意味着完全摒弃了复杂键过滤的价值,我们只是回到简单的——“如果记录在任何地方包含这个特定值,请找到我”(现在唯一明显的解决方案似乎是为“入口已知”值的所有可能组合创建视图,例如:* * lastName => /view/_lastNameOnly、fisrtName * lastName => /view/_firstLastName 等。但这意味着很多索引如果有数百万个文档,维护起来可能会非常繁重..
      • 这使得无法独立于名字“John”搜索姓氏“John”。
      【解决方案4】:

      一个 couchdb 索引有一个单一的排序;您发出的密钥(整个密钥)。这显然是一种非常简单的索引,但这也使它很快。

      解决您的问题的一种常用方法如下;

      function(doc) {
        if (doc.userFirstName) {
          emit(["firstname", doc.userFirstName], null);
        }
        if (doc.userMiddleName) {
          emit(["middlename", doc.userMiddleName], null);
        }
        if (doc.userLastName) {
          emit(["lastname", doc.userLastName], null);
        }
      }
      

      然后,您可以使用 ?key=["lastname", "Mozart"] 查询 lastname = "Mozart" 的任何文档。

      与之前的建议相比,这样做的优势是您不会从名字或中间名也是莫扎特的文档中得到误报。

      【讨论】:

      • 但是您对查询的建议是什么,即给定(已知)名字和姓氏?这样,您将获得所有“Amadeus”记录,然后是所有“Mozart”记录 - 您应该额外组合/过滤所有这些记录,但不仅仅是那些带有“Amadeus”“Mozart”的记录.我觉得没有意义,对不起
      • 对于更复杂的查询,您需要更复杂的索引。有允许 AND 查询的 couchdb-lucene(免责声明:我写了它),还有 couchdb 2.0 的 dreyfus/clouseau lucene 集成(免责声明:我也写了)。
      • 我认为内置的查询/芒果可以处理?我不是很熟悉,但文档解释了它可以做什么。
      • 感谢您的解释。全文搜索对我的设置来说有点矫枉过正,但我​​决定实现“startsWidth”功能 - 用于部分名称搜索。所以我以简单的查找查询结束,它提供了相当广泛的输出(特别是如果名称以开头的字母开头 - a/A、b/B 等),然后只是以编程方式过滤输出。不是很完美但可行的解决方案。
      猜你喜欢
      • 2014-02-23
      • 2011-09-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-17
      • 2011-01-02
      • 2011-10-20
      相关资源
      最近更新 更多