如何在给定查询中搜索索引短语

时间:2014-11-04 16:50:56

标签: elasticsearch full-text-search text-parsing inverted-index

鉴于来自用户的自由形式查询,我试图确定它是否包含位置短语。

示例:给出自由形式查询“旧金山ca中的纽约风格披萨”,并给出包含位置短语的文档索引,例如“denver co”,“miami fl”,“new york city ny”,“san” francisco ca“,”paris france“等,匹配将是包含位置短语”san francisco ca“的文档。

包含位置短语的索引还包含允许的排列,在单独的文档中。在上面的例子中,我可能有“旧金山加州”,“旧金山加利福尼亚”,以及可能还有其他如“sf ca”,“bay area ca”等,所有这些都是索引中的单独文件。套管和标点符号将被放弃,因此查询“纽约风格的PIZZA,在旧金山,加州”将成为“旧金山咖啡馆的纽约风格披萨”。

我还应该提一下,如果有一种更好或必要的方法来索引位置,使其适用于给定类型的查询,例如在不同的字段中包含“city”,“state”和“country”,我也可以这样做,我对建议很开放。

到目前为止我尝试过:

  1. 普通旧匹配查询。似乎工作得最好,但忽略了订购......“旧金山ca”是一场比赛,而“ca francisco san”不应该匹配。也忽略了位置。
  2. 词组匹配。根本不起作用,因为输入查询中的额外术语(“new york style pizza in”)没有匹配。
  3. 多字段匹配,cross_fields选项。与匹配查询相同的问题;忽略排序和位置。尝试使用索引的版本,其中“city”和“state”等是不同的字段。
  4. 渗滤。无法开始工作。调用GET ... / _ percolate检索索引中的所有文档。此外,构建.percolator类型非常缓慢,最终崩溃了我的实例(JVM内存99%),同时使用批量api。我的数据库中有大约1M个位置,我认为对于过滤器来说太多了,它在120K左右的地方一直在崩溃。从我读过的内容来看,我不认为这是一个适合过滤器的用例,但不确定。
  5. 我没有尝试过,为什么:

    1. 带状疱疹。给定位置的术语数量是可变的(即“dallas texas”vs“san francisco california”vs“new york city new york”),带状疱疹似乎适用于特定数量的术语。
    2. QUERY_STRING。我不想要求用户在双引号内放置短语。我也不想要查询语言(OR,AND等)。另外,忽略了位置。
    3. 我花了3-4天的时间来解决这个问题,并且非常感谢一些温和的指导。示例查询/索引/映射会很棒,但即使只是让我知道我应该使用什么类型的查询(以及索引和映射)将会非常有帮助,所以我至少可以“咆哮正确的树”!

      我愿意将其他工具与ES结合使用,只要它们是开源的,免费提供的,并得到相当好的支持。用过的。位置数据库包含~1M记录。

      BONUS:我假设位置短语(如果有的话)将朝向查询的末尾。某种方式来感知或相应地提高结果将是伟大的。注意我不想将此作为绝对要求;如果用户提交查询“我希望旧金山披萨餐厅有纽约风格披萨”,那么前面描述的索引中唯一有效的位置短语是“旧金山ca”,应该是匹配。

      BONUS 2X:我有每个位置的人口信息。对于更高人口而言稍微提高结果的一些方法也会很棒(我已经尝试了使用field_value_factor函数和ln1p修饰符的function_score,它似乎运行良好,但不确定如果我最终使用过滤器将如何工作)。

      奖金3倍!:容纳轻微的错别字,例如“san francsco”会很棒。

      我正在使用ElasticSearch 1.3.2。

      谢谢!!

      编辑:为了清楚起见,我正在寻找一个短语搜索,当索引短语比查询短时,这里很好地描述,但遗憾的是没有完全解决:

      Solr: Phrase search when indexed phrase is shorter than the query

1 个答案:

答案 0 :(得分:0)

以下是一些建议,即使我有一些疑问,我理解你的要求是正确的。

基本思想是操纵您在索引(位置)中放置的内容,因为您希望匹配的内容大于实际存储在文档中的内容。此外,我想强调的是,我认为这不是一个黑白情况,你要么得到一个(正确)答案,要么根本没有答案。比赛总会有“得分”。

另一点是,您需要知道如何操纵您的位置,以便在您预测人们会使用哪些查询时,这些操作会在大多数情况下帮助您(不是所有案例)。更好的是,您对其执行的索引位置和操作的组合将为您提供更高的机会来匹配大多数查询。

以下是一些具体的想法:

  1. 使用带状疱疹。我相信这是唯一选项,让您拥有订购条款的概念。你说你有一个自由格式查询。这意味着在您的查询中,您只想将该查询放在其他内容中,而不是将其划分为术语,不删除停用词或类似的内容。这意味着您无法使用可以为您订购的span_near

    使用带状疱疹,您也可以摆脱用户输入“ca francisco san”的情况。

  2. 第一个位置操作理念:存储完整的位置名称(除了上面的带状疱疹)。对于那些实际完全匹配您的位置文档的查询,这将为您提供更高的分数。而且,由于我从您的示例中看到您有多个位置组合,因此“位置”索引的“质量”很有可能为您提供良好的匹配结果。

  3.   "settings": {
        "analysis": {
          "filter": {
            "my_shingle_filter": {
              "type": "shingle",
              "min_shingle_size": 2,
              "max_shingle_size": 2,
              "output_unigrams": true // this is true for situations where you have "paris france" in locations but user searches for "paris"
            }
          },
          "analyzer": {
            "my_shingle_analyzer": {
              "type": "custom",
              "tokenizer": "standard",
              "filter": [
                "lowercase",
                "my_shingle_filter"
              ]
            }
          }
        }
      },
      "mappings": {
        "locations": {
          "properties": {
            "name": {
              "type": "string",
              "analyzer": "my_shingle_analyzer",
              "fields": {
                "full": {
                  "type": "string",
                  "analyzer": "keyword"
                }
       }}}}}
    
    1. 使用mapping transformation来提高您的地理位置索引的质量。这意味着,我上面提到过的那些操作 - 他们会根据与查询条款相关的预测,为索引添加其他字段(就像上面的name.full一样)。

      第一个示例源自您的一个查询示例:“旧金山ca的纽约风格披萨”。对于索引中的每个位置,请添加另一个应具有in前缀的字段:in san franciscoin new york等。

    2. "transform": [
              {
              "script": "full_plus_in = 'in ' + ctx._source['name']; ctx._source['name.full_plus_in'] = full_plus_in",
              "lang": "groovy"
              }
      ...
      

      第二个示例是将places后缀添加到映射中的新字段。假设您的预测中经常会考虑像“旧金山地方新风格比萨饼”这样的查询:

      {"script": "full_plus_places = ctx._source['name'] + ' places'; ctx._source['name.full_plus_places'] = full_plus_places",
              "lang": "groovy"}
      

      将所有内容放在一起是一个初步的映射:

      {
        "settings": {
          "analysis": {
            "filter": {
              "my_shingle_filter": {
                "type": "shingle",
                "min_shingle_size": 2,
                "max_shingle_size": 2,
                "output_unigrams": true
              }
            },
            "analyzer": {
              "my_shingle_analyzer": {
                "type": "custom",
                "tokenizer": "standard",
                "filter": [
                  "lowercase",
                  "my_shingle_filter"
                ]
              }
            }
          }
        },
        "mappings": {
          "locations": {
            "transform": [
              {
              "script": "full_plus_in = 'in ' + ctx._source['name']; ctx._source['name.full_plus_in'] = full_plus_in",
              "lang": "groovy"
              },
              {"script": "full_plus_places = ctx._source['name'] + ' places'; ctx._source['name.full_plus_places'] = full_plus_places",
              "lang": "groovy"}
              ],
            "properties": {
              "name": {
                "type": "string",
                "analyzer": "my_shingle_analyzer",
                "fields": {
                  "full": {
                    "type": "string",
                    "analyzer": "keyword"
                  },
                  "full_plus_in": {
                    "type": "string",
                    "analyzer": "keyword"
                  },
                  "full_plus_places": {
                    "type": "string",
                    "analyzer": "keyword"
                  }
                }
              }
            }
          }
        }
      }
      

      测试数据:

      {"index":{}}
      {"name":"denver co"}
      {"index":{}}
      {"name":"miami fl"}
      {"index":{}}
      {"name":"new york city ny"}
      {"index":{}}
      {"name":"san francisco ca"}
      {"index":{}}
      {"name":"paris france"}
      {"index":{}}
      {"name":"bay area ca"}
      {"index":{}}
      {"name":"dallas texas"}
      {"index":{}}
      {"name":"san francisco california"}
      {"index":{}}
      {"name":"new york city new york"}
      

      示例查询:

      {
        "query": {
          "bool": {
            "must": [
              {
                "match": {
                  "name": "i want san francisco ca places having new york style pizza"
                }
              }
            ],
            "should": [
              {"match": {
                "name.full": "i want san francisco ca places having new york style pizza"
              }},
              {"match": {
                "name.full_plus_in": "i want san francisco ca places having new york style pizza"
              }},
              {"match": {
                "name.full_plus_places": "i san francisco ca places having new york style pizza"
              }}
            ]
          }
        }
      }
      

      第一个匹配的位置应该是最好的(考虑得分)。