【问题标题】:CouchDB (Cloudant) - get unique results for a "starts with" search on multiple fieldsCouchDB (Cloudant) - 为多个字段的“开始于”搜索获得独特的结果
【发布时间】:2014-12-08 13:13:42
【问题描述】:

我正在尝试从“开头为”或使用相同的搜索键搜索多个字段中获得唯一的结果。
结果应该是原始文档之外的唯一子节点。
等效的 SQL 应该类似于:
SELECT DISTINCT Name, Email FROM users WHERE Name LIKE 'key%' OR Email LIKE 'key%'

我的视图函数:

Map: function(doc){
        if (doc.userDetails.firstName) 
            emit([doc.userDetails.firstName.toLowerCase(), doc.userDetails],  null);
        if (doc.userDetails.lastName) 
            emit([doc.userDetails.lastName.toLowerCase(), doc.userDetails],  null);
    }

Reduce: function(key, values, rereduce) {
    return null;
}

问题在于,在键与每个用户文档的两个字段匹配的情况下,它会返回重复的结果。 例如:

JSON 文档:

{
  "userDetails":{
    "email": "johnny@domain.com",
    "name":  "John Smith"
  },
  "privateFields":  { ... }
}

查询:myView?reduce=true&group=true&startkey=["joh"]&endkey=["joh\ufff0", {}]&inclusive_end=true

以上返回重复项,一次用于匹配电子邮件字段,另一次用于匹配名称字段。
有什么建议吗?

【问题讨论】:

  • 我认为不可能在数据库端删除重复项。

标签: mapreduce couchdb unique cloudant


【解决方案1】:

我建议为此使用search index。如何从 FirstName 和 LastName 生成 Name?假设它们是串联的,您的索引将类似于:

function(doc) {
    if(doc.userDetails.email) {
         index("email", doc.userDetails.email);
    }
    var name = doc.userDetails.firstName + " " + doc.userDetails.lastName;
    name = name.trim();
    if(name) {
         index("name", name);
    }
}

那么查询将是 myIndex?q=email:joh* OR name:joh*

默认情况下,值将被标记为非字母数字字符 - 您可能希望使用字段级分析器配置来获得适当的结果。

【讨论】:

  • 谢谢!是否可以返回特定的子节点作为结果?我的意思是,直接获取“userDetails”对象,而不是通过请求整个文档,然后在客户端/服务器端对其进行过滤?
  • 如果 userDetails 很小,您可以选择索引它并使用 {"store":true} 选项,以便在结果中返回它。如果它很大,那么我建议返回整个文档并在客户端进行过滤。您还可以考虑将 userDetails 保存为不同的文档。
  • 不幸的是,Cloudant 中的值只能是字符串、布尔值或数字,否则文档将不会被索引。 - 一个昂贵的解决方案是 JSON.stringify() ...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-30
  • 1970-01-01
  • 2019-12-01
  • 2017-07-28
  • 2019-11-15
相关资源
最近更新 更多