ES的简单前缀提示搜索(Movies案例)

要根据电影的名字做前缀搜索,前缀搜索的字段属性应该要使用 completion, 这个时候需要我们自定义mapping,但是mapping定义过于复杂。所以我们的处理方式,就是先用少量的样本数据导入到ES中去,然后ES会自动的帮我们生成一个mapping, 然后可以获取到该mapping信息,再将之前的索引删除,根据之前的mapping进行修改,然后在导入数据。

第一步, 使用logstash正常的导入数据,

第二步,获取mapping信息: GET movies

{
  "movies" : {
    "aliases": {},
    "mappings" : {
      "properties" : {
        "@version" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "genre" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "id" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "title": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above" : 256
            }
          }
        },
        "year" : {
          "type" : "long"
        }
      }
    },
    "settings" : {
      "index" : {
        "creation_date" : "1625041865073",
        "number_of_shards" : "1",
        "number_of_replicas" : "1",
        "uuid" : "NCAfXDw3RBCRPt-RT436XA",
        "version" : {
          "created" : "7080199"
        },
        "provided_name" : "movies"
      }
    }
  }
}

第三步,将movies索引删除掉:delete movies

第四步,根据 “第二步” 获取到mapping信息,重新创建mapping.

PUT movies
{
	 "aliases" : { },
    "mappings" : {
      "properties" : {
        "@version" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "genre" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "id" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "title" : {
           # 将title的类型改成 completion, 其他额外信息删除
          "type" : "completion"
        },
        "year" : {
          "type" : "long"
        }
      }
    }
}

第五步,重新到如 movies.csv 数据集,还是最开始的导入方式

第六步,在kibana中实现前缀的搜索提示

// 建议搜索
GET movies/_search
{
  // "_source": "" 表示不查询任何的字段的数据
  "_source": "", 
  // 这种前缀搜索相当于建议一样
  "suggest": {
    // 自己取的建议的名字,名字随意
    "title_prefix_suggest": {
      "prefix": "We",
      "completion": {
        "field": "title",
        "size": 10
      }
    }
  }
}

第七步,编写Java代码的实现

// 往IOC容器中注入ES的客户端工具
@Configuration
public class RestClientConfig extends AbstractElasticsearchConfiguration {

    // 创建一个ES的客户端
    @Override
    @Bean
    public RestHighLevelClient elasticsearchClient() {
        final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
                .connectedTo("localhost:9200")
                .build();

        return RestClients.create(clientConfiguration).rest();
    }
}
@RestController
@RequestMapping("/mv")
public class MovieController {

    private RestHighLevelClient restHighLevelClient;

    /*
     * @Qualifier("elasticsearchClient") 主要是针对容器中有多个 bean, 可以通过 @Qualifier 将那个bean注入进来
     * @param elasticsearchClient
     */
    public MovieController(@Qualifier("elasticsearchClient") RestHighLevelClient restHighLevelClient) {
        this.restHighLevelClient = restHighLevelClient;
    }
    @GetMapping("/pre")
    public List<String> prefixSuggest(String prefix) throws IOException {
        // 使用Request构建在kibana中写的 查询
        Request request  = new Request("GET", "movies/_search");

        String suggestJson = String.format("{" +
                "  \"_source\": \"\", " +
                "  \"suggest\": {" +
                "    \"title_prefix_suggest\": {" +
                "      \"prefix\": \"%s\"," +
                "      \"completion\": {" +
                "        \"field\": \"title\"" +
                "      }" +
                "    }" +
                "  }" +
                "}", prefix);

        request.setJsonEntity(suggestJson);

        // 发送请求, 返回结果
        Response response = restHighLevelClient.getLowLevelClient().performRequest(request);

        // 返回的json字符串, 就是于 kibana中得到的json数据
        String responseJson = EntityUtils.toString(response.getEntity());

        JSONObject jsonObject = JSONObject.parseObject(responseJson);
        // 获取 kibana 中的 "suggest" 对应的json对象
        JSONObject suggest = jsonObject.getJSONObject("suggest");

        // 就是搜索的信息与结构
        JSONObject resultInfo = suggest.getJSONArray("title_prefix_suggest").getJSONObject(0);

        JSONArray options = resultInfo.getJSONArray("options");

        // 给定长度, 是一种优化的策略
        List<String> results = new ArrayList<>(options.size());

        for(int i = 0; i < options.size(); i++) {
            // opt 就是我们需要的数据
            JSONObject opt = options.getJSONObject(i);

            results.add(opt.getString("text"));
        }

        return results;
    }
}
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐