Logstash - > Elasticsearch - 更新非规范化数据

时间:2017-02-01 09:27:28

标签: elasticsearch logstash event-sourcing denormalization

用例说明

我们有一个关系数据库,其中包含有关我们日常运营的数据。目标是允许用户使用全文搜索引擎搜索重要数据。数据被规范化,因此不是最佳形式来进行全文查询,因此想法是对数据的子集进行非规范化并将其实时复制到Elasticsearch,这使我们能够创建快速准确的搜索应用程序

我们已经有一个系统可以启用Event Sourcing数据库操作(插入,更新,删除)。事件仅包含已更改的列和主键(在更新时,我们不会获得整行)。 Logstash已经收到每个事件的通知,因此该部分已经处理完毕。

实际问题

现在我们遇到了问题。由于计划是对数据进行非规范化,因此我们必须确保将父对象的更新传播到Elasticsearch中的非规范化子对象。我们如何配置logstash来执行此操作?

实施例

假设我们在Elasticsearch中维护了Employees的列表。每个Employee都分配给Company。由于数据是非规范化的(为了加快搜索速度),每个Employee也包含Company的名称和地址。更新会更改Company的名称 - 我们如何配置logstash以更新分配给Employees的所有Company中的公司名称?

补充说明

@Darth_Vader: 我们面临的问题是,我们得到一个Company已更改的事件,但我们想在Elasticsearch中修改Employee类型的文档,因为它们本身包含有关公司的数据。您的回答希望我们每个Employee都会收到一个事件,但事实并非如此。

也许这会让它更清晰。我们在Elasticsearch有3名员工:

{type:'employee',id:'1',name:'Person 1',company.cmp_id:'1',company.name:'Company A'}
{type:'employee',id:'2',name:'Person 2',company.cmp_id:'1',company.name:'Company A'}
{type:'employee',id:'3',name:'Person 3',company.cmp_id:'2',company.name:'Company B'}

然后在源数据库中进行更新。

UPDATE company SET name = 'Company NEW' WHERE cmp_id = 1;

我们在logstash中收到一个事件,其中的内容如下:

{type:'company',cmp_id:'1',old.name:'Company A',new.name:'Company NEW'}

然后应将其传播到Elasticsearch,以便生成的员工:

{type:'employee',id:'1',name:'Person 1',company.cmp_id:'1',company.name:'Company NEW'}
{type:'employee',id:'2',name:'Person 2',company.cmp_id:'1',company.name:'Company NEW'}
{type:'employee',id:'3',name:'Person 3',company.cmp_id:'2',company.name:'Company B'}

请注意,字段 company.name 已更改。

1 个答案:

答案 0 :(得分:0)

对于我发布的here,我建议采用类似的解决方案,即使用http输出插件,以便通过对Employee索引的查询调用来发布更新。查询将如下所示:

POST employees/_update_by_query
{
  "script": {
    "source": "ctx._source.company.name = params.name",
    "lang": "painless",
    "params": {
      "name": "Company NEW"
    }
  },
  "query": {
    "term": {
      "company.cmp_id": "1"
    }
  }
}

因此您的Logstash配置应如下所示:

input {
  ... 
}
filter {
  mutate {
    add_field => {
      "[script][lang]" => "painless"
      "[script][source]" => "ctx._source.company.name = params.name"
      "[script][params][name]" => "%{new.name}"
      "[query][term][company.cmp_id]" => "%{cmp_id}"
    }
    remove_field => ["host", "@version", "@timestamp", "type", "cmp_id", "old.name", "new.name"]
  }
}
output {
  http {
    url => "http://localhost:9200/employees/_update_by_query"
    http_method => "post"
    format => "json"
  }
}