在Elasticsearch中存储HTML文档

时间:2017-04-07 09:27:23

标签: html elasticsearch

方案

我有HTML文档,比方说:电子邮件。我想将它们存储在弹性搜索中并搜索HTML电子邮件的明文。

问题

Elasticsearch也将索引所有HTML标记和属性。我不希望这样。我想搜索span,如果它是纯文本,而不是html元素。例如,<span>span</span>可能是一个匹配,但不是<span>some other content</span>

问题

您是否建议在文档中存储HTML剥离字段和HTML字段?或者我应该在S3上存储HTML文档,而是在弹性搜索中留下剥离的HTML版本?它甚至有意义吗

老实说,我不知道如果弹性搜索索引HTML文档会发生什么,但我可以想象它还会索引div和spans以及所有属性。这些是我完全不寻找的东西。所以:在这里解决问题的任何建议都会很棒!

我现在在做什么?

在我将文档存储在ES中之前,我检查文档类型是否存在索引。如果没有,我创建一个具有给定映射的集合。映射看起来像这样

{
    "analysis": {
        "analyzer": {
            "htmlStripAnalyzer": {
                "type": "custom",
                "tokenizer": "standard",
                "filter": "standard",
                "char_filter": [
                    "html_strip"
                ]
            }
        }
    },
    "mappings": {
        "${type}": {
            "dynamic_templates": [
                {
                    "_metadata": {
                        "path_match": "_metadata.*",
                        "mapping": {
                            "type": "keyword"
                        }
                    }
                }
            ],
            "properties": {
                "_tags": {
                    "type": "nested",
                    "dynamic": true
                }
            }
        }
    }
}

警告:忽略现有的映射。这与我的意图无关。他们就在那里。

我正在用文档类型替换$ {type},让我们说emails告诉ES不要将HTML内容编入索引会是什么样子?

2 个答案:

答案 0 :(得分:6)

完整的测试用例:

DELETE /test
PUT /test
{
  "settings": {
    "analysis": {
      "analyzer": {
        "htmlStripAnalyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": ["standard","lowercase"],
          "char_filter": [
            "html_strip"
          ]
        }
      }
    }
  },
  "mappings": {
    "test": {
      "properties": {
        "html": {
          "type": "text",
          "analyzer": "htmlStripAnalyzer"
        }
      }
    }
  }
}

POST /test/test/1
{
  "html": "<td><tr>span<td></tr>"
}
POST /test/test/2
{
  "html": "<span>whatever</span>"
}
POST /test/test/3
{
  "html": "<html> <body> <h1 style=\"font-family: Arial\">Test</h1> <span>More test</span> </body> </html>"
}

POST /test/_search
{
  "query": {
    "match": {
      "html": "span"
    }
  }
}

POST /test/_search
{
  "query": {
    "match": {
      "html": "body"
    }
  }
}

POST /test/_search
{
  "query": {
    "match": {
      "html": "more"
    }
  }
}

答案 1 :(得分:1)

默认情况下,如果在索引过程中找到任何新字段,Elasticsearch将动态添加新字段(请参阅this):

  

当Elasticsearch遇到文档中以前未知的字段时,它使用动态映射来确定字段的数据类型,并自动将新字段添加到类型映射中。

要禁用此行为(有关详细信息,请参阅doc),最简单的方法是将dynamic置于 false (阻止自动创建)或严格(抛出异常并且不创建新文档)。在这种情况下,您需要明确地为要保留在_tags部分内的标记写入映射,并预先解析HTML文档以仅将您感兴趣的标记提供给Elasticsearch。

因此,假设您有以下HTML文档:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>A simple example</title>
</head>
<body>
  <div>
    <p><span class="ref">A sentence I want to reference from this HTML document</span></p>
    <p><span class="">Something less important</span></p>
</body>
</html>

你想要的第一件事是Elasticsearch中的静态映射,我会做以下(假设ref是一个字符串):

PUT html
{

"mappings": {
  "test":{
    "dynamic": "strict",
    "properties": {
      "ref":{
        "type": "string"
      }
    }
  }
}

现在,如果您尝试以这种方式添加文档,它将会成功:

PUT html/test/1
{
  "ref": "A sentence I want to reference from this HTML document"
}

但这不会成功:

PUT html/test/2
{
  "ref": "A sentence I want to reference from this HTML document",
  "some_field": "Some field"
}

现在唯一剩下的就是解析HTML以检索“ref”字段,并创建上述查询(使用您喜欢的任何语言,Java,Python ......)

修改:实际上要存储HTML而不对其进行索引,在地图中您只需将index设置为否(请参阅here):

"_tags": {
          "type": "nested",
          "dynamic": true,
          "index": "no"
         }