使用Elasticsearch,创建一个在30分钟内推测出口袋妖怪的搜索引擎

日本のゲーム会社エイチームの「引越し侍・エイチームコネクト Advent Calendar 2019」の22日目の担当は、@hinoraです!これが2回目です!

? 示范

? 这是什么?

我会根据输入的宝可梦名字推测并显示相似的宝可梦。

在示威活动中得出以下的结果。

INOUTピカチュウライチュウ, エレキッド, ピチュー

机制

我要使用Elasticsearch的相似查询(More Like This Query)。

使用“更多相似结果”查询,可以从指定的文档或关键词中提取出相似的内容。

作为推荐系统,虽然精度不如机器学习等方法,但非常适合希望以一定精度简单实现”推荐文章”或”推荐商品”等场景!

✊ 亲自实践一下

准备Elasticsearch和Kibana。

请使用Docker Compose进行部署,需要注意它会占用相当多的内存。同时,我们也会安装kuromoji插件。

请您为音量相关的目录进行适当创建。

docker-compose.yml:docker-compose.yml
version: ‘2’

services:
elasticsearch:
build: elasticsearch
volumes:
– ./docker/es/data:/usr/share/elasticsearch/data
– ./docker/es/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
ports:
– 9200:9200
environment:
– discovery.type=single-node
– bootstrap.memory_lock=true
– “ES_JAVA_OPTS=-Xms512m -Xmx512m”
kibana:
image: docker.elastic.co/kibana/kibana:7.5.1
ports:
– 5601:5601

volumes:
elasticsearch-data:
driver: local

elasticsearch/Dockerfile:
FROM docker.elastic.co/elasticsearch/elasticsearch:7.5.1

RUN elasticsearch-plugin install analysis-kuromoji

2. 数据的获取和导入

本次我们将使用每只宝可梦的以下参数。

パラメータ名説明例name名前オオタチcolor色茶色flavorText説明文ははおやは ほそながい からだでこどもを…eggGroupタマゴグループりくじょうgenus○○ポケモンどうながポケモン

如果有数据集就好了,但是没有找到适合的,所以我克隆了PokeAPI并用docker-compose构建了它,从API中将数据取出来。

获得

我将使用JS进行fetch请求。

/** 指定したIDのポケモンの情報を取得する
  *  - これを800くらいまで取ってきます
  *  - jsonのArrayをJSON.stringifyしながらjoin("\n")するとあとで使いやすいです
  */
const fetchPokemonById = async id => {
  const res = await fetch(`http://localhost/api/v2/pokemon-species/${id}`).then(res => res.json());
  const { flavor_text: flavorText } = findByLanguage(res.flavor_text_entries);
  const { name } = findByLanguage(res.names);
  const { genus } =  findByLanguage(res.genera);
  return {
    id,
    name,
    flavorText,
    genus,
    color: res.color.name,
    eggGroup: res.egg_groups[0].name,
  };
};

整形手术然后注入

本次支持的导入格式有JSON和CSV,但我们选择使用JSON。

不过要说JSON并不是普通的JSON文件,而是称为拥有换行分隔的JSON。

这种格式就如它的名字所示,由多个通过换行符分隔的JSON组成。

{"id":1,"name":"フシギダネ","flavorText":"~~~~~~~","genus":"たねポケモン","color":"green","eggGroup":"plant"}
{"id":2,"name":"フシギソウ","flavorText":"~~~~~~~","genus":"たねポケモン","color":"green","eggGroup":"plant"}
// ...

请进入Kibana的Machine Learning – Data Visualizer – Import data页面,导入之前创建的JSON文件。

选择文件后按下导入按钮,会询问如何处理索引,因此我们将创建一个名为”pokemon”的索引。

准备工作已经完成了!

3. 发出一个类推的查询

我打算试着去捕捉一个类似于”不可思议种子”的宝可梦。
事先在Kibana的Discover等页面上记下文档的ID。

当我复制了文档的ID之后,我将在Kibana的Dev Tools中尝试发送More Like This Query。

推测查询

使用”like”命令来指定与”fields”进行比较的参数名称。

GET pokemon/_search
{
  "_source": "name", 
  "query": {
    "more_like_this": {
      "fields": [
        "name",
        "flavorText",
        "color",
        "eggGroup",
        "genus"
      ],
      "like": [
        {
          "_id": "Th1ALW8BEmAvY9Q5hJWt" // フシギソウのドキュメントID
        }
      ]
      ,
      "min_term_freq": 1,
      "max_query_terms": 12
    }
  }
}

结果

{
  // ~ 省略 ~
    "hits" : [
      {
        "_index" : "pokemon",
        "_type" : "_doc",
        "_id" : "UB1ALW8BEmAvY9Q5hJWt",
        "_score" : 23.025995,
        "_source" : {
          "name" : "フシギバナ"
        }
      },
      {
        "_index" : "pokemon",
        "_type" : "_doc",
        "_id" : "DR1ALW8BEmAvY9Q5hJa0",
        "_score" : 22.985844,
        "_source" : {
          "name" : "キマワリ"
        }
      },
   // ...

使用这个,「用于推测宝可梦的搜索引擎」已经完成了!如果在Web服务等中使用,只要从喜欢的客户端发送请求并显示即可。

顺便提一下,在工作中我们使用了一个叫做Chewy的Rails宝石。
它非常易于使用,我推荐使用!

额外内容:视角方面

这次我选择直接从前端实现,但因为有官方客户端,所以比我想象的要简单。

const es = require('elasticsearch');
const client = new es.Client({
  host: 'localhost:9200',
});

// 全てのドキュメントを取得するクエリを投げる
function fetchAll() {
  return client.search({
    index: 'pokemon',
    body: {
      size: 50,
      query: { match_all: {} },
    },
  });
}

已完成的产品

因为沙包和皮卡丘都属于老鼠类宝可梦,所以沙包比老鼠宝可梦皮丘得分高嘞,哈哈。

如果将类型作为比较参数的对象纳入考虑,可能会更加精确!

总结

当听到“推荐系统”这个词时,可能会给人一种很难的印象,但是如果使用“更多类似的查询”功能,就可以轻松地实现推荐。

此外,还可以通过修改max_query_terms等参数来调整以提高准确性。

请务必尝试一下!


明天轮到 @halkt 亲出场了!期待你的精彩表演喔!

bannerAds