【问题标题】:Solr string field search with special characters带有特殊字符的 Solr 字符串字段搜索
【发布时间】:2018-06-01 03:52:42
【问题描述】:

我刚刚开始研究 Solr。有一个电话字段,它已在如下架构中定义

<field docValues="true" indexed="true" multiValued="true" name="phones" stored="true" type="StrField"/>

据我了解,字符串字段将尝试进行完全匹配,但用户可以使用任何格式来搜索带有特殊字符(如 (111) 111-1111)的电话号码。因此,我使用 ClientUtils.escapeQueryChars 为特殊字符添加斜线,但搜索未产生任何结果。我一直试图理解为什么以及是否有任何标准不能为字符串字段转义特殊字符?我不认为标记器很重要,因为它是字符串字段并且我使用 edismax 解析器。有什么想法吗?

【问题讨论】:

  • 我不确定您所说的“但用户可以使用任何格式搜索电话号码”是什么意思。字符串字段将给出完全匹配。它必须在每一种方式上都相同才能获得成功。因此,如果您已将 (111) 111-1111 编入索引,那么这是唯一会在搜索时获得成功的值。
  • 我明白这一点。但如果我逃避角色不应该工作。当我调试我在查询中设置的最终值是 (111)111\-1111 时,我没有看到任何结果。但是,如果我搜索为 1111111111,那么我会看到它。所以我的问题是,如果我转义特殊字符,为什么我看不到预期的结果。
  • StrField 在您的架构中定义为什么?你索引的价值是多少? 1111111111 不应与值为 (111) 111-1111 的字符串字段匹配。 debugQuery 显示什么?分析页面显示什么?
  • StrField 定义为` `。我知道 1111111111 与 (111)111-1111 不匹配,但在转义特殊字符 (111)111\-1111 后,我不确定为什么看不到匹配项。在分析页面中,我看到 start:0、end:12、type:word、position:1 和 raw_bytes 中的值。我看到的一件事是,如果我在电话字段中使用特殊字符(括号和连字符)并使用模糊搜索(~),那么它在转义特殊字符后会起作用。但是如果我不使用模糊搜索,那么我将无法搜索完全匹配的特殊字符。
  • 您的查询字符串到底是什么样的?

标签: java search solr


【解决方案1】:

我使用 Solr 7.3.1 复制了您所询问的内容,并且可以确认,只要您正确地避开 (),您就会得到您正在寻找的命中。

架构

  • id:字符串
  • 电话:字符串(多值、文档值、索引、存储)

文档

{
  "id":"doc1",
  "phones":["(111) 111-1111"],
  "_version_":1602190176246824960
},
{
  "id":"doc2",
  "phones":["111 111-1111"],
  "_version_":1602190397829808128
},
{
  "id":"doc3",
  "phones":["111 (111)-1111"],
  "_version_":1602190400002457600
}

查询

/select?q=phones:\(111\)\ 111-1111

{
    "id":"doc1",
    "phones":["(111) 111-1111"],
    "_version_":1602190176246824960}]
}

/select?debugQuery=on&amp;q=phones:111\ 111-1111

{
    "id":"doc2",
    "phones":["111 111-1111"],
    "_version_":1602190397829808128}]
}

/select?debugQuery=on&amp;q=phones:1111111111

"response":{"numFound":0,"start":0,"docs":[]}

行为与描述完全相同 - 仅完全匹配。

使用PatternReplaceCharFilterFactory获得您想要的行为

让我们创建一个自定义字段类型,以删除任何不是数字或字母的内容:

curl -X POST -H 'Content-type:application/json' --data-binary '{
  "add-field-type" : {
     "name":"phoneStripped",
     "class":"solr.TextField",
     "positionIncrementGap":"100",
     "analyzer" : {
        "charFilters":[{
           "class":"solr.PatternReplaceCharFilterFactory",
           "replacement":"",
           "pattern":"[^a-zA-Z0-9]"
        }],
        "tokenizer":{
           "class":"solr.KeywordTokenizerFactory" 
        },
     }
  }
}' http://localhost:8983/solr/foo/schema

然后我们使用这个新的字段类型创建一个名为 phone_stripped 的新字段(您可以在 UI 中执行此操作),并重新索引我们的文档 - 现在使用新的字段名称:

  {
    "id":"doc1",
    "phone_stripped":"(111) 111-1111"
  },
  {
    "id":"doc3",
    "phone_stripped":"111 (111)-1111"
  },
  {
    "id":"doc2",
    "phone_stripped":"111 111-1111"
  }

然后我们只搜索1111111111

"response":{"numFound":3,"start":0,"docs":[ .. all our docs ..]

使用之前的搜索,phone_stripped:\(111\)\ 111-1111

"response":{"numFound":3,"start":0,"docs":[ .. all our docs ..]

为了确保我们没有以无法形容的方式破坏东西,让我们搜索phone_stripped:\(111\)\ 111-1112

"response":{"numFound":0,"start":0,"docs":[]

【讨论】:

  • 好的。我想我现在明白了。因此,在您的示例中,如果文档存储的电话号码为 1111111111,并且如果用户想使用 (111)111-1111 之类的括号进行搜索,那么我假设我必须在搜索之前替换或删除特殊字符,是是正确的还是更好的想法?
  • 这是正确的 - 这就是为什么我在第一条评论中问您是否真的想要完全匹配。如果无论用户如何键入电话号码都想匹配,您可以使用 PatternReplaceCharFilter 删除任何不是数字的字符 ([^0-9])(或者如果您允许字母,则任何不是字母或号码 - [^a-zA-Z0-9])。
  • 我已经用一个例子更新了我的答案,展示了你可能想要什么。
  • 谢谢!很有帮助。
猜你喜欢
  • 2012-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-24
  • 2016-05-30
  • 1970-01-01
相关资源
最近更新 更多