2

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

示例:给定自由格式查询“旧金山加利福尼亚州的纽约风格披萨”,并给出包含诸如“丹佛公司”、“迈阿密佛罗里达州”、“纽约市纽约市”、“旧金山加利福尼亚州”等位置短语的文档索引, "paris france" 等,匹配的将是包含位置短语 "san francisco ca" 的文档。

包含位置短语的索引还包含允许的排列,在单独的文档中。在上面的例子中,我可能有“san francisco ca”、“san francisco california”,可能还有“sf ca”、“bay area ca”等其他文档,它们都是索引中的单独文档。前面将丢弃大小写和标点符号,因此查询“纽约风格的 PIZZA, in san francisco, ca”将变为“new york style Pizza in san francisco ca”。

我还应该提到,如果有更好或需要的方法来索引位置以使其适用于给定类型的查询,例如在不同的字段中包含“城市”、“州”和“国家”,我可以这样做也是如此,我非常愿意接受建议。

到目前为止我已经尝试过:

  1. 普通的旧匹配查询。似乎效果最好,但忽略了排序...“san francisco ca”是匹配的,而“ca francisco san”不应该匹配。也忽略了位置。
  2. 短语匹配。根本不起作用,因为由于输入查询中的额外术语(“纽约风格的披萨店”),我没有得到任何匹配项。
  3. 多字段匹配,cross_fields 选项。与匹配查询相同的问题;忽略排序和位置。这是尝试使用“城市”和“州”等是不同字段的索引版本。
  4. 渗透。根本无法上班。调用 GET .../_percolate 检索索引中的所有文档。此外,构建 .percolator 类型非常缓慢,最终使我的实例崩溃(JVM 内存 99%),而使用批量 api 这样做。我的数据库中有大约 100 万个位置,我认为对于 percolator 来说太多了,它在大约 120K 位置始终崩溃。根据我的阅读,我认为这不是渗滤器的合适用例,但不确定。

我没有尝试过的,以及为什么:

  1. 带状疱疹。给定位置的术语数量是可变的(即“dallas texas”与“san francisco california”与“new york city new york”),并且带状疱疹似乎适用于特定数量的术语。
  2. 请求参数。我不想要求用户将短语放在双引号内。我也不想要查询语言(OR、AND 等)。此外,忽略位置。

我已经花了 3-4 天的时间来解决这个问题,并且非常感谢一些温和的指导。示例查询/索引/映射会很棒,但即使只是让我知道我应该使用哪种类型的查询(以及索引和映射)也会非常有帮助,所以我至少可以“找到正确的树”!

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

奖励:我假设位置短语(如果有)将接近查询的结尾。某种方式来感知这一点或相应地提升结果会很棒。请注意,我不想将此作为绝对要求;如果用户提交查询“我想要 san francisco ca 披萨店有纽约风格的披萨”,那么给定前面描述的索引的唯一有效的位置短语是“san francisco ca”,这应该是匹配的。

奖励 2X:我有每个位置的人口信息。对于更高的人口,稍微提高结果的某种方法也会很棒(我已经尝试了 function_score 与 field_value_factor 函数和 ln1p 修饰符,它似乎工作得很好,但不确定如果我最终使用 percolator 会如何工作)。

奖金 3X!:容纳轻微的拼写错误,例如“san francsco”会很棒。

我正在使用 ElasticSearch 1.3.2。

谢谢你!!

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

Solr:索引短语短于查询时的短语搜索

4

1 回答 1

0

这里有一些建议,即使我有一些疑问,我也理解您的要求是正确的。

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

另一点是,您需要知道如何操作您的位置,这样,根据您预测人们将使用的查询,这些操作将在大多数情况下(并非所有情况)对您有所帮助。更好的说法是,索引位置和您对它们执行的操作的组合将使您有更高的机会匹配大多数查询。

以下是一些具体的想法:

  1. 使用带状疱疹。我相信这是您拥有有序术语概念的唯一选择。你说你有一个自由形式的查询。这意味着在您的查询中,您只想将该查询放在其他地方,不被划分为术语,不删除停用词或类似的东西。这意味着您不能使用可以给您命令的span_near 。

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

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

  "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. 使用映射转换来提高位置索引的质量。name.full意思是,我上面提到的那些操作 - 他们将根据与查询术语相关的预测向您的索引添加额外的字段(就像上面一样)。

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

"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"
        }}
      ]
    }
  }
}

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

于 2014-11-07T10:03:38.270 回答