前言:
当处理海量数据时,单纯的mysql oracle 以及sql查询已经无法满足我们在效率上的需求,elasticSearch 是当下一款热门的实时搜索引擎基于lucense的搜索服务器,使用它可以完成近乎实时的数据查询。并且支持权重搜索,全文搜索等方式进行查询。
目录
一、准备开发环境
二、常用操作
三、查询结果高亮显示
四、效果展示
五、后记:
org.elasticsearch
elasticsearch
5.5.3
org.elasticsearch.client
transport
5.5.3
org.apache.logging.log4j
log4j-api
2.7
由于代码是从我的项目中拿出来的,没有用到的代码直接删除就可以了
1.工具类ESutils,用于client客户端的关闭,创建
package com.rupeng.utlis;
import java.net.InetAddress;
import java.net.UnknownHostException;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import com.rupeng.service.config.ConfigInfo;
/**
* 用来创建关闭es客户端
* @author 2016wlw2 徐塬峰
* 创建时间:2018年7月24日 上午10:32:28
*/
public class EsUtils {
/**
* 獲取ElasticSearch
*/
public synchronized static Client getClient() {
Settings settings = Settings.builder().
put("client.transport.sniff", true)
.build();//自动嗅探其他集群的ip 如果有则加入
InetSocketTransportAddress master = null;
try {
master = new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9300);
TransportClient client = new PreBuiltTransportClient(settings).addTransportAddress(master);
return client;
}
catch (UnknownHostException e) {
e.printStackTrace();
throw new RuntimeException("elasticSearch Client init error 连接创建失败"+e);
}
}
/**
* 用于关闭elasticSearch
*/
public static void closeClient(Client client){
if(null != client){
try {
client.close();
} catch (Exception e) {
throw new RuntimeException("连接关闭失败");
}
}
}
1.SSM注入client客户端
代码以及其引用的方式
/**
* 实现自动注入elasticserach
* @author Ray
*/
@Configuration
public class ElasticSerachConfig {
//自动注入client
@Bean(name="client")
public TransportClient esClint() throws UnknownHostException
{
Settings settings = Settings.builder().build();
InetSocketTransportAddress master=new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"),9300);
TransportClient client =new PreBuiltTransportClient(settings).addTransportAddress(master);
return client;
}
}
@Resource(name = "client")
private TransportClient client;
2.基本操作(查询,增加,删除,修改)
2.1基本查询
public List search (String query,int number)
{
long start =System.currentTimeMillis();
if(client==null)
{
client =ESUtils.getTransportClient();
}
// 定义查询用到的类型,单独定义
String Type[]=new String[]{INDEXKEYTEMPLATE.TYPE_COURSES
,INDEXKEYTEMPLATE.TYPE_NEWS,INDEXKEYTEMPLATE.TYPE_SEGMENTS};
SearchRequestBuilder responsebuilder = client.prepareSearch(INDEX_NAME).setTypes(Type);
List hitsList=new ArrayList();
String Key[]=new String[]{CoursesIndexKey.NAME,
NewsIndexKey.TITLE,
SegmentsIndexKey.NAME,
BbsIndexKey.TITLE
};//查询用到的关键字,单独定义
SearchResponse myresponse = responsebuilder
.setQuery(QueryBuilders.multiMatchQuery(query, Key))
.setFrom(0)//from 从哪条开始 ,可用于分页操作
.setSize(number).setExplain(true).execute().actionGet();//number为查询的条数
SearchHits hits = myresponse.getHits();
for (int i = 0; i < hits.getTotalHits(); i++) // getHits()当前查询页的结果
{
SearchHit hit = hits.getHits()[i];
String jsonStr=hit.getSourceAsString();
logger.info("索引库的数据:" +jsonStr );
hitsList.add(jsonStr);
}
long end=System.currentTimeMillis();
logger.debug("为您找到相关结果约"+hits.getTotalHits()+"个----"+"耗时"+(end-start)/1000+"秒");
return hitsList;
}
2.2根据id来获取数据
public String getOneById(String INDEX_TYPE,Long id)
{
if(client==null)
{
client =ESUtils.getTransportClient();
}
GetResponse getResponse = client.prepareGet(INDEX_NAME,INDEX_TYPE,String.valueOf(id))
.execute()
.actionGet();
if(getResponse!=null)
{
logger.debug(getResponse.getSourceAsString());
String jsonStr=getResponse.getSourceAsString();
return jsonStr;
}
else
{
logger.error("找不到id对应的数据");
return null;
}
}
2.3精确匹配 方法和1类似 不过替换为MatchPhraseQuery
2.4创建操作 分批次提交数据
基本思路,从数据库获取数据,并将数据库里的数据插入到elasticSearch搜索服务器,数据量由于比较大,所以
采用了分批次提交
public boolean createAll(List pojoList,String INDEX_TYPE) throws InterruptedException, ExecutionException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException
{
long start =System.currentTimeMillis();
Gson gson = new Gson();
if(client==null)
{
client =ESUtils.getTransportClient();
}
BulkRequestBuilder bulkRequest = client.prepareBulk();
for(T pojo:pojoList)
{
// 通过反射获得方法getID 设置主键
// pojo.getClass().getMethod("getId", Long.class);
Field FieldId=pojo.getClass().getDeclaredField("id");
//设置可读写
FieldId.setAccessible(true);
Object id = FieldId.get(pojo);
IndexRequest request=client.prepareIndex(INDEX_NAME,INDEX_TYPE).setId(String.valueOf(id))
.setSource(gson.toJson(pojo),XContentType.JSON).request();
bulkRequest.add(request);
if(bulkRequest.numberOfActions()==1000)
{
bulkRequest.execute().get();//get的实现就是调用executeget
bulkRequest=client.prepareBulk();//创建一个对象
logger.debug("已提交1000条数据");
}
}
if(bulkRequest.numberOfActions()>0)//最后一批不足一千的再提交一次
{
bulkRequest.execute().get();//
long end=System.currentTimeMillis();
logger.debug("最后一批已提交,"+"耗时"+(end-start)/1000+"秒");
}
ESUtils.close(client);
return true;
}
2.6删除操作
2.6.1删除整个索引库
/** 注意 :该操作会删除整个索引库
*
* @param INDEX_NAME
*/
public void deleteIndex(String INDEX_NAME)
{
if(client==null)
{
client =ESUtils.getTransportClient();
}
TransportResponse resp=client.admin().indices().prepareDelete(INDEX_NAME).get();
if(resp!=null)
{
logger.debug("索引库删除成功");
ESUtils.close(client);
}
else
{
logger.error("索引删除失败");
ESUtils.close(client);
}
ESUtils.close(client);
}
2.6.2 删除指定id
如果需要删除单个类型里的多个文档,则可以使用for循环删除 或者其他 只要思路正确就ok
/**
* 根据id的值删除文档
* @param id
* @param INDEX_TYPE
* @throws UnknownHostException
*/
public void removeOneById(Long id,String INDEX_TYPE)
{
if(client==null)
{
client =ESUtils.getTransportClient();
}
DeleteResponse response = client.prepareDelete(INDEX_NAME, INDEX_TYPE, String.valueOf(id)).get();
String index = response.getIndex();
String type = response.getType();
String typeId = response.getId();
long version = response.getVersion();
logger.debug("删除一条文档"+index + " : " + type + ": " + typeId + ": " + version);
ESUtils.close(client);
}
2.6.3 删除指定分类
public boolean deleteType(String indexName, String type) {
client = EsUtils.getClient();
QueryBuilder builder = QueryBuilders.typeQuery(type);
DeleteByQueryAction.INSTANCE.newRequestBuilder(client).source(indexName).filter(builder).execute().actionGet();
EsUtils.closeClient(client);
return true;
}
2.7插入一条数据
/**
* 创建一条或者更新索引
* @param esMixedDataDto
* @return
*/
public boolean upsertDocument(T pojo,String INDEX_TYPE) {
client =ESUtils.getTransportClient();
Gson gson = new Gson();
Field FieldId;
try {
FieldId = pojo.getClass().getDeclaredField("id");
FieldId.setAccessible(true);
Object id = FieldId.get(pojo);
IndexResponse indexRes=client.prepareIndex(INDEX_NAME,INDEX_TYPE).setId(String.valueOf(id))
.setSource(gson.toJson(pojo),XContentType.JSON).get();
String index = indexRes.getIndex();
String type = indexRes.getType();
String typeId = indexRes.getId();
logger.debug("插入成功"+index + " : " + type + ": " + typeId + ": " );
ESUtils.close(client);
return true;
}
catch (NoSuchFieldException | SecurityException e) {
// TODO Auto-generated catch block
logger.warn("插入失败");
e.printStackTrace();
} catch (IllegalArgumentException e) {
logger.warn("插入失败");
e.printStackTrace();
} catch (IllegalAccessException e) {
logger.warn("插入失败");
e.printStackTrace();
}
return false;
}
java代码实现高亮显示
public Map hlSearch (String query,int number)
{
long start =System.currentTimeMillis();
if(client==null)
{
client =ESUtils.getTransportClient();//创建连接
}
String Type[]=new String[]{INDEXKEYTEMPLATE.TYPE_COURSES
,INDEXKEYTEMPLATE.TYPE_NEWS,INDEXKEYTEMPLATE.TYPE_SEGMENTS};
SearchRequestBuilder requestBuilder = client.prepareSearch(INDEX_NAME).setTypes(Type);
String Key[]=new String[]{
CoursesIndexKey.NAME,
NewsIndexKey.TITLE,
SegmentsIndexKey.NAME,
BbsIndexKey.TITLE,
};
SearchRequestBuilder searchRequestBuilder = requestBuilder
.setQuery(QueryBuilders.multiMatchQuery(query, Key))
.setFrom(20). //从那行开始
setSize(30).setExplain(true);//.actionGet()==get()
Map msgMap = new HashMap();
HighlightBuilder highlightBuilder = new HighlightBuilder().field("*").requireFieldMatch(false);
highlightBuilder.preTags("");
highlightBuilder.postTags("");
searchRequestBuilder.highlighter(highlightBuilder);
SearchResponse response = searchRequestBuilder.get();
List
Controller层
@RequestMapping(value="/hlsearch.do",method=RequestMethod.POST)
public @ResponseBody AjaxResult hlsearch(String text)
{
Map msg=searchService.hlSearch(text, 10);
for(Map.Entry entry :msg.entrySet())
{
System.out.println("Key:"+entry.getKey()+"Value:"+entry.getValue());
}
return new AjaxResult("Success",msg);
}
前端代码(vue.js)
search:function()
{
var that=this;
that.results=[];
axios.post('./hlsearch.do?text='+this.text)
.then(function(resp){
var results=resp.data.data.itemsList;
if(results.length==0)
{
alert("未找到匹配结果");
that.message="找到约0 条结果!";
}
else
{
that.message="找到约 "+results.length+" 条结果!";
for(var i=0;i
1.前端不要用ajax请求来获取数据,异步获取数据在并发环境下 会导致前端展示结果 错乱的情况,所以
建议大家使用返回视图的方式来展示数据,这样效果更好
2.生产环境下 client必须关闭,否则会导致资源泄露,内存溢出等问题。但是client 获取连接的时间较慢依然是一个大问题
获取一个client平均需要2秒左右 ,这样查询快的意义就没有了。所以应该尽量采用es连接池的方式来创建client 客户端。
连接池代码(参考)
package com.rupeng.elasticpools;
import java.net.InetAddress;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import com.rupeng.service.config.ConfigInfo;
/**
* 每一次创建es的连接都太耗费时间 我们应该自己去封装一个es的连接池
* @author 2016wlw2 徐塬峰 创建时间:2018年7月31日 上午10:00:32
*/
public class ElasticSearchFactory implements PooledObjectFactory{
private int elasticPool_maxTotal = Integer.parseInt(ConfigInfo.elasticPool_maxTotal.trim());
private int elasticPool_maxIdle = Integer.parseInt(ConfigInfo.elasticPool_maxIdle.trim());
private String elasticSearch_addr ="127.0.0.1";// ConfigInfo.elasticSearch_addr;
private int elasticSearch_port = Integer.parseInt(ConfigInfo.elasticSearch_port.trim());
private int elasticPool_minIdle = Integer.parseInt(ConfigInfo.elasticPool_minIdle.trim());
private int maxWaitMilis = Integer.parseInt(ConfigInfo.elasticPool_maxWaitMilis.trim());
private static GenericObjectPool pool;// 连接池
static
{
ElasticSearchFactory fa=new ElasticSearchFactory();
fa.createElasticSearchPool();
}
/**
* 创建连接池
*/
public void createElasticSearchPool()
{
ElasticSearchFactory fac = new ElasticSearchFactory();// 创建工厂
GenericObjectPoolConfig conf = new GenericObjectPoolConfig();// 配置文件
conf.setMaxTotal(elasticPool_maxTotal);// 设置线程池中最大的数量
conf.setMaxIdle(elasticPool_maxIdle);// 设置最大的空闲时间
conf.setMinIdle(elasticPool_minIdle);// 设置最小空闲连接
conf.setMaxWaitMillis(maxWaitMilis);// 设置最大等待时间
try {
//先创建三个连接
fac.makeObject();
fac.makeObject();
fac.makeObject();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
pool = new GenericObjectPool(fac, conf);// 创建连接池
}
// 模拟对象创建的过程
@Override // 通过ElastcSearchConnections 来创建连接
public PooledObject makeObject() throws Exception {
Settings settings = Settings.builder().put("client.transport.sniff", true).build();// 自动嗅探其他集群的ip
InetSocketTransportAddress master = null;
master = new InetSocketTransportAddress(InetAddress.getByName(elasticSearch_addr), elasticSearch_port);
TransportClient client = new PreBuiltTransportClient(settings).addTransportAddress(master);
System.out.println("太棒了,对象创建成功了!");
return new DefaultPooledObject(client);
}
public static GenericObjectPool getPool() {
return pool;
}
//销毁
@Override
public void destroyObject(PooledObject p) throws Exception {
p.getObject().close();
}
@Override
public boolean validateObject(PooledObject p) {
// TODO Auto-generated method stub
return false;
}
@Override
public void activateObject(PooledObject p) throws Exception {
}
@Override
public void passivateObject(PooledObject p) throws Exception {
System.out.println("passivate Object"+p.toString());
}
}
3.elasticSearch 已不再维护transport 客户端 尽量使用rest client客户端来进行开发。。。。。。。
以上就是我归纳的对elasticsearch5.5.3版本的java实现,如果有哪些地方有问题,欢迎大家留言指正,感激不尽!