因为项目开发使用的是Java语言, 项目的开发架构是Spring MVC+ maven的jar包管理, 所以今天重点说说ES 5.4.3 的Java API的源码实战
1. pom.xml文件增加依赖:
1 <!-- elasticsearch --> 2 <dependency> 3 <groupId>org.elasticsearch</groupId> 4 <artifactId>elasticsearch</artifactId> 5 <version>5.4.3</version> 6 </dependency> 7 8 <dependency> 9 <groupId>org.elasticsearch.client</groupId> 10 <artifactId>transport</artifactId> 11 <version>5.4.3</version> 12 </dependency> 13 14 <dependency> 15 <groupId>org.apache.logging.log4j</groupId> 16 <artifactId>log4j-core</artifactId> 17 <version>2.6.2</version> 18 </dependency> 19 <dependency> 20 <groupId>org.apache.logging.log4j</groupId> 21 <artifactId>log4j-api</artifactId> 22 <version>2.6.2</version> 23 </dependency> 24 <dependency> 25 <groupId>org.apache.logging.log4j</groupId> 26 <artifactId>log4j-1.2-api</artifactId> 27 <version>2.6.2</version> 28 </dependency>
2. 首先我们创建一个获取Client 的Factory类,该类中的配置信息是配置在项目的.properties文件中
注意端口号通常为9300,这个是ES为java保留的默认端口号。
1 package com.cs99lzzs.elasticsearch; 2 3 import org.elasticsearch.client.Client; 4 import org.elasticsearch.client.transport.TransportClient; 5 import org.elasticsearch.common.network.InetAddresses; 6 import org.elasticsearch.common.settings.Settings; 7 import org.elasticsearch.common.transport.InetSocketTransportAddress; 8 import org.elasticsearch.common.transport.TransportAddress; 9 import org.elasticsearch.transport.client.PreBuiltTransportClient; 10 import org.springframework.beans.factory.annotation.Value; 11 import org.springframework.context.annotation.Bean; 12 import org.springframework.context.annotation.Configuration; 13 14 @Configuration 15 public class ElasticsearchFactory { 16 17 @Value("${elasticsearch.ips}") 18 private String ES_IPS; 19 20 @Value("${elasticsearch.cluster.name}") 21 private String CLUSTER_NAME; 22 23 @Value("${elasticsearch.port}") 24 private int ES_PORT; 25 26 private static TransportClient transportClient = null; 27 28 /** 29 * 获取esClient实例 30 * @return 31 */ 32 @Bean(name = "esClient") 33 public Client getESClient(){ 34 35 /** 36 * 1:通过 setting对象来指定集群配置信息 37 */ 38 if (transportClient == null) { 39 Settings settings = Settings.builder() 40 .put("cluster.name", CLUSTER_NAME) 41 .put("client.transport.sniff", true) 42 .build(); 43 44 transportClient = new PreBuiltTransportClient(settings); 45 46 String esIps[] = ES_IPS.split(","); 47 for (String esIp : esIps) {//添加集群IP列表 48 TransportAddress transportAddress = new InetSocketTransportAddress(InetAddresses.forString(esIp), ES_PORT); 49 transportClient.addTransportAddresses(transportAddress); 50 } 51 } 52 return transportClient; 53 } 54 }
3. 索引创建的Controller类, 主要是2个定时任务:全量更新和增量更新
另外一个重点就是:创建mapping。 该mapping包含了一个搜索提示的字段
1 package com.cs99lzzs.elasticsearch; 2 3 import java.net.InetAddress; 4 import java.sql.Timestamp; 5 6 import javax.annotation.Resource; 7 import javax.servlet.http.HttpServletRequest; 8 import javax.servlet.http.HttpServletResponse; 9 10 import org.apache.commons.lang.StringUtils; 11 import org.apache.log4j.Logger; 12 import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest; 13 import org.elasticsearch.client.Client; 14 import org.elasticsearch.client.Requests; 15 import org.elasticsearch.client.transport.TransportClient; 16 import org.elasticsearch.common.settings.Settings; 17 import org.elasticsearch.common.transport.InetSocketTransportAddress; 18 import org.elasticsearch.common.xcontent.XContentBuilder; 19 import org.elasticsearch.common.xcontent.XContentFactory; 20 import org.elasticsearch.transport.client.PreBuiltTransportClient; 21 import org.springframework.beans.factory.annotation.Value; 22 import org.springframework.scheduling.annotation.Scheduled; 23 import org.springframework.stereotype.Controller; 24 import org.springframework.web.bind.annotation.RequestMapping; 25 import org.springframework.web.bind.annotation.RequestMethod; 26 import org.springframework.web.bind.annotation.RequestParam; 27 import org.springframework.web.bind.annotation.ResponseBody; 28 29 import redis.clients.jedis.Jedis; 30 31 import com.showjoy.data.page.JedisClient; 32 import com.showjoy.elasticsearch.service.ProductIndexService; 33 34 @Controller 35 public class CreateIndexController { 36 37 private static Logger logger = Logger.getLogger(CreateIndexController.class); 38 39 private final static String _REDIS_DB_STORE_TIME_KEY_NAME = "es.shop.index.update.time"; 40 private final static String _PRODUCT_INDEX_TIME_KEY = "es.shop.product.time"; 41 42 @Resource 43 private ProductIndexService productIndexService; 44 45 @Resource(name="esClient") 46 Client esClient; 47 48 @Value("${elasticsearch.index}") 49 private String CLUSTER_INDEX; 50 51 @Value("${elasticsearch.type}") 52 private String CLUSTER_TYPE; 53 /** 54 * 55 * @author chenxu 56 * 2017年7月13日 下午6:25:54 57 * @param request 58 * @param response 59 * @return 60 */ 61 @ResponseBody 62 @RequestMapping(value={"/", "/info"}, method = RequestMethod.GET) 63 public String info(HttpServletRequest request, HttpServletResponse response) { 64 return "success"; 65 } 66 67 @ResponseBody 68 @RequestMapping(value = "/es/index", method = RequestMethod.GET) 69 public void productIndex(@RequestParam(required = false, defaultValue = "2016-01-05 00:00:00") String fromTime) { 70 productIndexService.createIndex(Timestamp.valueOf(fromTime)); 71 } 72 73 74 /** 75 * 创建mapping 76 * @author chenxu 77 * 2017年7月10日 下午2:10:24 78 * @param indices 79 * @param mappingType 80 * @throws Exception 81 */ 82 public static void createMapping(String index, String type, Client client) 83 throws Exception { 84 XContentBuilder builder = XContentFactory.jsonBuilder() 85 .startObject() 86 .startObject(type) 87 .startObject("properties"); 88 //搜索字段: text类型, ik_max_word分词 89 builder.startObject("brandZhName").field("type", "text").field("analyzer", "ik_max_word").field("store", "yes").endObject(); 90 builder.startObject("brandEnName").field("type", "text").field("analyzer", "ik_max_word").field("store", "yes").endObject(); 91 builder.startObject("brandAliases").field("type", "text").field("analyzer", "ik_max_word").field("store", "yes").endObject(); 92 builder.startObject("aliases").field("type", "text").field("analyzer", "ik_max_word").field("store", "yes").endObject(); 93 builder.startObject("zhName").field("type", "text").field("analyzer", "ik_max_word").field("store", "yes").endObject(); 94 builder.startObject("enName").field("type", "text").field("analyzer", "ik_max_word").field("store", "yes").endObject(); 95 //排序字段 96 builder.startObject("price").field("type", "float").field("store", "yes").endObject();//现价 97 builder.startObject("salesVolume").field("type", "integer").field("store", "yes").endObject();//销量 98 builder.startObject("commission").field("type", "float").field("store", "yes").endObject();//收益 99 //其他字段 100 /* integer */ 101 builder.startObject("id").field("type", "integer").field("store", "yes").endObject(); 102 builder.startObject("spuId").field("type", "integer").field("store", "yes").endObject(); 103 builder.startObject("inventory").field("type", "integer").field("store", "yes").endObject(); 104 builder.startObject("brandId").field("type", "integer").field("store", "yes").endObject(); 105 builder.startObject("cateId").field("type", "integer").field("store", "yes").endObject(); 106 builder.startObject("cateScope").field("type", "integer").field("store", "yes").endObject(); 107 builder.startObject("vipShopId").field("type", "integer").field("store", "yes").endObject(); 108 109 /* boolean */ 110 builder.startObject("isDelete").field("type", "boolean").field("store", "yes").endObject(); 111 builder.startObject("triable").field("type", "boolean").field("store", "yes").endObject(); 112 builder.startObject("isTrialPack").field("type", "boolean").field("store", "yes").endObject(); 113 builder.startObject("searchable").field("type", "boolean").field("store", "yes").endObject(); 114 builder.startObject("isHaitao").field("type", "boolean").field("store", "yes").endObject(); 115 builder.startObject("isSupplier").field("type", "boolean").field("store", "yes").endObject(); 116 117 /* keyword */ 118 builder.startObject("spuStatusId").field("type", "keyword").field("index", "no").field("store", "yes").endObject(); 119 builder.startObject("image").field("type", "keyword").field("index", "no").field("store", "yes").endObject(); 120 builder.startObject("isSuit").field("type", "keyword").field("index", "no").field("store", "yes").endObject(); 121 builder.startObject("unit").field("type", "keyword").field("index", "no").field("store", "yes").endObject(); 122 builder.startObject("sex").field("type", "keyword").field("index", "no").field("store", "yes").endObject(); 123 builder.startObject("brandPathName").field("type", "keyword").field("index", "no").field("store", "yes").endObject(); 124 builder.startObject("brandImage").field("type", "keyword").field("index", "no").field("store", "yes").endObject(); 125 126 builder.startObject("brandName").field("type", "keyword").field("store", "yes").endObject(); 127 builder.startObject("cateName").field("type", "keyword").field("store", "yes").endObject(); 128 builder.startObject("cateNameTree").field("type", "keyword").field("store", "yes").endObject(); 129 builder.startObject("salesPromotionTag").field("type", "keyword").field("store", "yes").endObject(); //促销标签 130 131 builder.startObject("perfumeType").field("type", "keyword").field("index", "no").field("store", "yes").endObject(); 132 builder.startObject("brandScope").field("type", "keyword").field("index", "no").field("store", "yes").endObject(); 133 134 /* float */ 135 builder.startObject("volume").field("type", "float").field("store", "yes").endObject(); 136 builder.startObject("originalPrice").field("type", "float").field("store", "yes").endObject(); 137 builder.startObject("discount").field("type", "float").field("store", "yes").endObject(); 138 139 /* date */ 140 builder.startObject("gmtCreate").field("type", "date").field("store", "yes").endObject(); 141 builder.startObject("gmtModified").field("type", "date").field("store", "yes").endObject(); 142 143 /* suggester */ 144 builder.startObject("suggestName").field("type","completion").endObject(); 145 // builder.startObject("suggestBrandZhName").field("type","completion").field("analyzer","ik_max_word") 146 // .field("search_analyzer","ik_max_word").endObject(); 147 148 149 //结束 150 builder.endObject().endObject().endObject(); 151 152 PutMappingRequest mapping = Requests.putMappingRequest(index) 153 .type(type).source(builder); 154 client.admin().indices().putMapping(mapping).actionGet(); 155 156 } 157 158 /** 159 * 全量更新商品索引任务,凌晨4点执行 160 * @author chenxu 161 * 162 **/ 163 @Scheduled(cron = "0 0 4 * * ?") 164 @ResponseBody 165 @RequestMapping(value = "/search/index/allproduct", method = RequestMethod.GET) 166 public void indexProducts(){ 167 Timestamp updateTime = new Timestamp(0); 168 saveTimestamp(_PRODUCT_INDEX_TIME_KEY, new Timestamp(System.currentTimeMillis())); 169 productIndexService.createIndex(updateTime); 170 } 171 172 /** 173 * 增量更新商品索引 ,每隔5分钟执行 174 * @author chenxu 175 * 176 **/ 177 @Scheduled(cron = "0 */5 * * * ?") 178 @ResponseBody 179 @RequestMapping(value = "/search/index/product", method = RequestMethod.GET) 180 public void indexProduct() { 181 Timestamp updateTime = getTimestamp(_PRODUCT_INDEX_TIME_KEY); 182 long currentTimeMillis = System.currentTimeMillis(); 183 productIndexService.createIndex(updateTime); 184 saveTimestamp(_PRODUCT_INDEX_TIME_KEY, new Timestamp(currentTimeMillis)); 185 } 186 187 private static Timestamp getTimestamp(String name) { 188 Timestamp timestamp = null; 189 try { 190 Jedis jedis = JedisClient.getJedis(); 191 jedis.select(1); 192 String timestr = jedis.hget(_REDIS_DB_STORE_TIME_KEY_NAME, name); 193 JedisClient.returnResource(jedis); 194 if (StringUtils.isNotBlank(timestr)) { 195 timestamp = Timestamp.valueOf(timestr); 196 } else { 197 timestamp = new Timestamp(0); 198 } 199 } catch (Exception e) { 200 logger.error("elasticsearch获取索引最新更新时间失败,检查存储redis是否出现问题", e); 201 timestamp = new Timestamp(System.currentTimeMillis()); 202 } 203 return timestamp; 204 } 205 206 private void saveTimestamp(String name, Timestamp timestamp) { 207 try { 208 Jedis jedis = JedisClient.getJedis(); 209 jedis.select(1); 210 jedis.hset(_REDIS_DB_STORE_TIME_KEY_NAME, name, timestamp.toString()); 211 JedisClient.returnResource(jedis); 212 } catch (Exception e) { 213 logger.error("存储索引最新更新时间失败,检查存储redis是否出现问题", e); 214 } 215 } 216 217 218 public static void main(String[] args) { 219 Settings settings = Settings.builder() 220 .put("cluster.name", "showjoy-shop-search") 221 .put("client.transport.sniff", true) 222 .build(); 223 224 TransportClient tc = new PreBuiltTransportClient(settings); 225 try { 226 227 tc.addTransportAddress( 228 new InetSocketTransportAddress(InetAddress.getByName("192.168.0.124"), 9300)); 229 230 createMapping("item_index", "shop_item", tc); 231 // 去数据库中扫描达人店商品的待搜索字段 232 // SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 233 // Map<String, Object> source = new HashMap<String, Object>(); 234 // source.put("name", "酒"); 235 // source.put("cateId", 5); 236 // source.put("brandId", 15); 237 // source.put("salesVolume", 250); 238 // source.put("price", 55.00d); 239 // source.put("income", 5.5d); 240 // source.put("createDate", sdf.format(new Date())); 241 // 242 // // 增加 243 // IndexResponse addResponse = tc 244 // .prepareIndex("item_index", "shop_item", "1") 245 // .setSource(source).get(); 246 // 247 // if (addResponse != null 248 // && addResponse.getShardInfo().getSuccessful() == 1) { 249 // if (Result.CREATED.equals(addResponse.getResult())) { 250 // System.err.println("create success"); 251 // } else { 252 // System.err.println("update success"); 253 // } 254 // } else { 255 // System.err.println(JSON.toJSONString(addResponse)); 256 // } 257 258 } catch (Exception e) { 259 e.printStackTrace(); 260 } 261 if (tc != null) { 262 tc.close(); 263 } 264 265 System.err.println("finished"); 266 } 267 }
4. 先在head插件中创建你自己的index名字,执行main函数, 就能在它下面创建相应的mapping。
批量写入数据,请看下篇: http://www.cnblogs.com/cs99lzzs/p/7212474.html