Я всегда чувствовал, что в блогах есть недостатки, но недавно я обнаружил, что очень проблематично найти блог, который я писал раньше, когда блогов больше, поэтому, как бэкенд-разработчик, я чувствую, что у меня достаточно еды и одежды. сам, и на этот раз функция поиска полного текста в блоге Elasticsearch фактический бой, здесь я хотел бы поблагодарить сервер, спонсируемый «Хуэй Ge».
Выбор инструмента полнотекстового поиска
Как мы все знаем, существует множество инструментов, поддерживающих полнотекстовый поиск, таких как Lucene, solr, Elasticsearch и т. д. По сравнению с другими инструментами сообщество Elasticsearch явно более активно, и проблемы решаются относительно легко. можно использовать спокойный интерфейс, предоставляемый Elasticsearch. Это более удобно, что также является важной причиной выбора Elasticsearch для арендодателя. Конечно, память, занимаемая Elasticsearch, относительно велика, и облачный сервер 2G арендодателя также растягивается.
Миграция данных с MySQL на Elasticsearch
Эта функция относительно проста. Она заключается в регулярном обновлении данных из MySQL в Elasticsearch. Изначально арендодатель планировал написать инструмент переноса данных, но я вспомнил, что DataX, используемый арендодателем для переноса данных, был очень хорошим. Я прочитал официальный документ. Он по-прежнему поддерживается, но арендодатель просто не запускал его. Причина в том, что облачного сервера с памятью 2G недостаточно. Для запуска DataX light требуется более 1G памяти, поэтому арендодатель может найти только другой способ. Друзья, интересующиеся DataX, могут посмотреть еще одну статью арендодателяАли автономный инструмент синхронизации данных DataX вытаптывает запись.
Говоря о языках, которые могут экономить память, друзья могут вспомнить о популярном в последнее время голанге, и да, хозяин дома тоже об этом подумал. В конце концов, арендодатель использовал инструмент под названием go-mysql-elasticsearch, который представляет собой инструмент для переноса данных из MySQL в Elasticsearch с помощью golang. Здесь арендодатель не будет подробно останавливаться на конкретном процессе строительства, если вы заинтересованы, пожалуйста, продолжайте.go-mysql-elasticsearch,Кроме того При построении среды Elasticsearch следует учитывать, что объем памяти машины, на которой установлен Elasticsearch, должен быть больше или равен 2G, иначе он может не запуститься.
Кроме того, следует отметить, что функция binlog mysql должна быть включена при использовании go-mysql-elasticsearch.Идея go-mysql-elasticsearch для достижения синхронизации данных заключается в том, чтобы монтировать себя на MySQL в качестве подчиненного устройства MySQL, так что вы можете Легко синхронизировать данные с Elasticsearch в режиме реального времени.На машине, где запущен go-mysql-elasticsearch, должен быть как минимум клиентский инструмент MySQL, иначе будет сообщено об ошибке. Предложение арендодателя состоит в том, чтобы развернуть MySQL с правами root на той же машине, потому что golang потребляет очень мало памяти и не окажет большого влияния. Следующее дается, когда арендодатель синхронизирует данные Файл конфигурации для go-mysql-elasticsearch:
# MySQL address, user and password
# user must have replication privilege in MySQL.
my_addr = "127.0.0.1:3306"
my_user = "root"
my_pass = "******"
my_charset = "utf8"
# Set true when elasticsearch use https
#es_https = false
# Elasticsearch address
es_addr = "127.0.0.1:9200"
# Elasticsearch user and password, maybe set by shield, nginx, or x-pack
es_user = ""
es_pass = ""
# Path to store data, like master.info, if not set or empty,
# we must use this to support breakpoint resume syncing.
# TODO: support other storage, like etcd.
data_dir = "./var"
# Inner Http status address
stat_addr = "127.0.0.1:12800"
# pseudo server id like a slave
server_id = 1001
# mysql or mariadb
flavor = "mysql"
# mysqldump execution path
# if not set or empty, ignore mysqldump.
mysqldump = "mysqldump"
# if we have no privilege to use mysqldump with --master-data,
# we must skip it.
#skip_master_data = false
# minimal items to be inserted in one bulk
bulk_size = 128
# force flush the pending requests if we don't have enough items >= bulk_size
flush_bulk_time = "200ms"
# Ignore table without primary key
skip_no_pk_table = false
# MySQL data source
[[source]]
schema = "billboard-blog"
# Only below tables will be synced into Elasticsearch.
tables = ["content"]
# Below is for special rule mapping
[[rule]]
schema = "billboard-blog"
table = "content"
index = "contentindex"
type = "content"
[rule.field]
title="title"
blog_desc="blog_desc"
content="content"
# Filter rule
[[rule]]
schema = "billboard-blog"
table = "content"
index = "contentindex"
type = "content"
# Only sync following columns
filter = ["title", "blog_desc", "content"]
# id rule
[[rule]]
schema = "billboard-blog"
table = "content"
index = "contentindex"
type = "content"
id = ["id"]
Сервис, реализующий функцию полнотекстового поиска
Для достижения функции полнотекстового поиска и предоставления услуг, веб-сервисов, необходимых арендодателю для создания веб-сервисов с использованием Spring Boot для Spring Boot, малозаинтересованные партнеры также могут ознакомиться с другой статьей арендодателя,Внедрить службу статистики блогов с помощью Spring Boot. Ну, хватит ерунды, пожалуйста, взгляните на код
Код реализации интерфейса, относительно простой код для получения параметров и вызова сервисного кода
@ApiOperation(value="全文检索接口", notes="")
@ApiImplicitParam(name = "searchParam", value = "博客搜索条件(作者,描述,内容,标题)", required = true, dataType = "String")
@RequestMapping(value = "/get_content_list_from_es", method = RequestMethod.GET)
public ResultCode<List<ContentsWithBLOBs>> getContentListFromEs(String searchParam) {
ResultCode<List<ContentsWithBLOBs>> resultCode = new ResultCode();
try {
LOGGER.info(">>>>>> method getContentListFromEs request params : {},{},{}",searchParam);
resultCode = contentService.getContentListFromEs(searchParam);
LOGGER.info(">>>>>> method getContentListFromEs return value : {}",JSON.toJSONString(resultCode));
} catch (Exception e) {
e.printStackTrace();
resultCode.setCode(Messages.API_ERROR_CODE);
resultCode.setMsg(Messages.API_ERROR_MSG);
}
return resultCode;
}
Реализован сервисный код, основная функция которого заключается в вызове класса инструмента es для выполнения полнотекстового поиска по описанию блога, автору, названию блога и содержанию блога.
@Override
public ResultCode<List<ContentsWithBLOBs>> getContentListFromEs(String searchParam) {
ResultCode resultCode = new ResultCode();
// 校验参数,参数不能为空
if (StringUtils.isBlank(searchParam)) {
LOGGER.info(">>>>>> params not be null");
resultCode.setMsg(Messages.INPUT_ERROR_MSG);
resultCode.setCode(Messages.INPUT_ERROR_CODE);
return resultCode;
}
String matchStr = "blog_desc=" + searchParam;
List<Map<String, Object>> result = ElasticsearchUtils.searchListData(BillboardContants.ES_CONTENT_INDEX,BillboardContants.ES_CONTENT_TYPE,BillboardContants.ES_CONTENT_FIELD,true,matchStr);
matchStr = "author=" + searchParam;
result.addAll(ElasticsearchUtils.searchListData(BillboardContants.ES_CONTENT_INDEX,BillboardContants.ES_CONTENT_TYPE,BillboardContants.ES_CONTENT_FIELD,true,matchStr));
matchStr = "title=" + searchParam;
result.addAll(ElasticsearchUtils.searchListData(BillboardContants.ES_CONTENT_INDEX,BillboardContants.ES_CONTENT_TYPE,BillboardContants.ES_CONTENT_FIELD,true,matchStr));
matchStr = "content=" + searchParam;
result.addAll(ElasticsearchUtils.searchListData(BillboardContants.ES_CONTENT_INDEX,BillboardContants.ES_CONTENT_TYPE,BillboardContants.ES_CONTENT_FIELD,true,matchStr));
List<ContentsWithBLOBs> data = JSON.parseArray(JSON.toJSONString(result),ContentsWithBLOBs.class);
LOGGER.info("es return data : {}",JSON.toJSONString(result));
resultCode.setData(data);
return resultCode;
}
Реализация кода класса инструмента es, используемая арендодателем, заключается в использовании java-клиента es для получения es.
/**
* 使用分词查询
*
* @param index 索引名称
* @param type 类型名称,可传入多个type逗号分隔
* @param fields 需要显示的字段,逗号分隔(缺省为全部字段)
* @param matchPhrase true 使用,短语精准匹配
* @param matchStr 过滤条件(xxx=111,aaa=222)
* @return
*/
public static List<Map<String, Object>> searchListData(String index, String type, String fields, boolean matchPhrase, String matchStr) {
return searchListData(index, type, 0, 0, null, fields, null, matchPhrase, null, matchStr);
}
/**
* 使用分词查询
*
* @param index 索引名称
* @param type 类型名称,可传入多个type逗号分隔
* @param startTime 开始时间
* @param endTime 结束时间
* @param size 文档大小限制
* @param fields 需要显示的字段,逗号分隔(缺省为全部字段)
* @param sortField 排序字段
* @param matchPhrase true 使用,短语精准匹配
* @param highlightField 高亮字段
* @param matchStr 过滤条件(xxx=111,aaa=222)
* @return
*/
public static List<Map<String, Object>> searchListData(String index, String type, long startTime, long endTime, Integer size, String fields, String sortField, boolean matchPhrase, String highlightField, String matchStr) {
SearchRequestBuilder searchRequestBuilder = client.prepareSearch(index);
if (StringUtils.isNotEmpty(type)) {
searchRequestBuilder.setTypes(type.split(","));
}
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
if (startTime > 0 && endTime > 0) {
boolQuery.must(QueryBuilders.rangeQuery("processTime")
.format("epoch_millis")
.from(startTime)
.to(endTime)
.includeLower(true)
.includeUpper(true));
}
//搜索的的字段
if (StringUtils.isNotEmpty(matchStr)) {
for (String s : matchStr.split(",")) {
String[] ss = s.split("=");
if (ss.length > 1) {
if (matchPhrase == Boolean.TRUE) {
boolQuery.must(QueryBuilders.matchPhraseQuery(s.split("=")[0], s.split("=")[1]));
} else {
boolQuery.must(QueryBuilders.matchQuery(s.split("=")[0], s.split("=")[1]));
}
}
}
}
// 高亮(xxx=111,aaa=222)
if (StringUtils.isNotEmpty(highlightField)) {
HighlightBuilder highlightBuilder = new HighlightBuilder();
//highlightBuilder.preTags("<span style='color:red' >");//设置前缀
//highlightBuilder.postTags("</span>");//设置后缀
// 设置高亮字段
highlightBuilder.field(highlightField);
searchRequestBuilder.highlighter(highlightBuilder);
}
searchRequestBuilder.setQuery(boolQuery);
if (StringUtils.isNotEmpty(fields)) {
searchRequestBuilder.setFetchSource(fields.split(","), null);
}
searchRequestBuilder.setFetchSource(true);
if (StringUtils.isNotEmpty(sortField)) {
searchRequestBuilder.addSort(sortField, SortOrder.DESC);
}
if (size != null && size > 0) {
searchRequestBuilder.setSize(size);
}
//打印的内容 可以在 Elasticsearch head 和 Kibana 上执行查询
LOGGER.info("\n{}", searchRequestBuilder);
SearchResponse searchResponse = searchRequestBuilder.execute().actionGet();
long totalHits = searchResponse.getHits().totalHits;
long length = searchResponse.getHits().getHits().length;
LOGGER.info("共查询到[{}]条数据,处理数据条数[{}]", totalHits, length);
if (searchResponse.status().getStatus() == 200) {
// 解析对象
return setSearchResponse(searchResponse, highlightField);
}
return null;
}
Наконец, используя тест веб-службы почтальона домовладельца, как показано ниже:
Яма, обнаруженная в процессе
Настройки токенизатора ИК
Здесь следует отметить, что версия Elasticsearch должна соответствовать версии токенизатора ik, иначе Elasticsearch сообщит об ошибке.
$ ./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.3.0/elasticsearch-analysis-ik-6.3.0.zip
Затем перезапуск Elastic автоматически загрузит только что установленный плагин.
Затем создайте новый индекс и укажите поля, которые нуждаются в сегментации слов. Этот шаг зависит от структуры данных, приведенные ниже команды предназначены только для этой статьи. В основном, все китайские поля, которые необходимо искать, должны быть установлены отдельно.
$ curl -X PUT 'localhost:9200/contentindex' -H 'Content-Type: application/json' -d '
{
"mappings": {
"content": {
"properties": {
"content": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
},
"title": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
},
"blog_desc": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
},
"author": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
}
}
}
}
}'
Приведенный выше код, первое новое имя для индекса ContentIndex, которое имеет имя для содержимого типа. Содержание имеет более хорошее поле, где только четыре поля указаны слово,content,title,blog_desc,author.
Все эти четыре поля написаны на китайском языке, а все типы текстовые, поэтому вам нужно указать китайский токенизатор вместо английского по умолчанию.
Настройки бинлога MySQL
Поскольку, когда арендодатель запускает go-mysql-elasticsearch клиентов MySQL, заканчивается экспортом версии MySQL, несоответствия данных на стороне сервера приводят к ошибке, окончательное урегулирование с помощью исходного автора go-mysql-elasticsearch, поэтому обязательно используйте сервер и клиент той же версии MySQL, из-за различных характеристик разных версий MySQL, это привело к тому, что данные экспорта go-mysql-elasticsearch немного отличаются.
резюме
Весь процесс относительно прост. Конечно, благодаря реализации этой функции арендодатель также имеет относительное понимание эс и научился новому навыку. Некоторых друзей может больше заинтересовать код всего проекта арендодателя. Я могу' Пока я не раскрываю его, и я внесу его, когда домовладелец завершит его.
Справочная статья
Добавить Автора
Оригинальная ссылка:Ооо, ооо.Студию заменили.К/статья/201...
Уведомление об авторских правах: не специальные операторы изначально созданы на этом сайте, укажите автор и исходную ссылку при перепечатании.