按部分网址匹配

时间:2016-06-07 16:18:21

标签: elasticsearch

我有两个索引 - 一个包含带有_id=<url of the document>的“文档”对象,例如http://site/folder/document_name.doc;另一个包含带有_id=<url of the folder>的“文件夹”对象,例如http://site/folder

在我的node.js脚本中,我需要将文档与文件夹匹配,即我搜索所有文件夹网址,然后为每个文件夹搜索所有文件,这些网址以文件夹url开头

我似乎无法构建正确的查询,该查询将返回_idhttp://site/folder开头的所有文档

有什么想法吗?

1 个答案:

答案 0 :(得分:3)

我认为更好的解决方案是不要将_id用于此问题。

相反,索引字段名为path(或您想要的任何名称),并使用Path Hierarchy Tokenizer查看一些创意代币过滤器。

这样您就可以使用Elasticsearch / Lucene来标记URL。

例如:https://site/folder被标记为两个标记:

  • site
  • site/folder

然后,您可以通过搜索正确的令牌来site文件夹中包含的任何文件或文件夹:site

PUT /test
{
  "settings": {
    "analysis": {
      "filter": {
        "http_dropper": {
          "type": "pattern_replace",
          "pattern": "^https?:/{0,}(.*)",
          "replacement": "$1"
        },
        "empty_dropper": {
          "type": "length",
          "min": 1
        },
        "qs_dropper": {
          "type": "pattern_replace",
          "pattern": "(.*)[?].*",
          "replacement": "$1"
        },
        "trailing_slash_dropper": {
          "type": "pattern_replace",
          "pattern": "(.*)/+$",
          "replacement": "$1"
        }
      },
      "analyzer": {
        "url": {
          "tokenizer": "path_hierarchy",
          "filter": [
            "http_dropper",
            "qs_dropper",
            "trailing_slash_dropper",
            "empty_dropper",
            "unique"
          ]
        }
      }
    }
  },
  "mappings": {
    "type" : {
      "properties": {
        "url" : {
          "type": "string",
          "analyzer": "url"
        },
        "type" : {
          "type": "string",
          "index": "not_analyzed"
        }
      }
    }
  }
}

您可能可能不想要我添加的trailing_slash_dropper。在那里使用lowercase令牌过滤器也是值得的,但这实际上可能会使某些URL令牌从根本上不正确(例如,mysite.com/bucket/AaDsaAe31AcxX可能真的关心这些字符的情况)。您可以使用_analyze端点将分析仪用于试驾:

GET /test/_analyze?analyzer=url&text=http://test.com/text/a/?value=xyz&abc=value

注意:我正在使用Sense,所以它为我做了URL编码。这将产生三个令牌:

{
  "tokens": [
    {
      "token": "test.com",
      "start_offset": 0,
      "end_offset": 15,
      "type": "word",
      "position": 0
    },
    {
      "token": "test.com/text",
      "start_offset": 0,
      "end_offset": 20,
      "type": "word",
      "position": 0
    },
    {
      "token": "test.com/text/a",
      "start_offset": 0,
      "end_offset": 22,
      "type": "word",
      "position": 0
    }
  ]
}

将它们捆绑在一起:

POST /test/type
{
  "type" : "dir",
  "url" : "https://site"
}

POST /test/type
{
  "type" : "dir",
  "url" : "https://site/folder"
}

POST /test/type
{
  "type" : "file",
  "url" : "http://site/folder/document_name.doc"
}

POST /test/type
{
  "type" : "file",
  "url" : "http://other/site/folder/document_name.doc"
}

POST /test/type
{
  "type" : "file",
  "url" : "http://other_site/folder/document_name.doc"
}

POST /test/type
{
  "type" : "file",
  "url" : "http://site/mirror/document_name.doc"
}

GET /test/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "url": "http://site/folder"
          }
        }
      ],
      "filter": [
        {
          "term": {
            "type": "file"
          }
        }
      ]
    }
  }
}

对此进行测试非常重要,这样您就可以看到匹配的内容以及这些匹配的顺序。当然,这会找到您希望它找到的文档(并将其置于顶部!),但它也会找到您可能不期望的其他文档,例如http://site/mirror/document_name.doc,因为它共享基本标记:{{1 }}。您可以使用一系列策略来排除这些文档如果排除它们很重要。

您可以利用标记化来执行类似Google的结果过滤,例如如何通过Google搜索特定域名:

  

匹配查询网站:elastic.co

然后您可以解析(手动)site并将site:elastic.co作为边界网址:

elastic.co

请注意,这与搜索网址不同。您明确说“只包含在其网址中包含此完全标记的文档”。您可以使用{ "term" : { "url" : "elastic.co" } } 等进一步,因为存在确切的令牌。但是,重要的是要注意,如果您要尝试site:elastic.co/blog,那么就找不到任何文档,因为在令牌过滤器的情况下该令牌不存在。