Sfoglia il codice sorgente

【代码迁移】实时更新索引

sunyj 9 anni fa
parent
commit
ea387a3eee
16 ha cambiato i file con 1128 aggiunte e 190 eliminazioni
  1. 51 0
      search-console/src/main/java/com/uas/search/console/controller/IndexController.java
  2. 0 15
      search-console/src/main/java/com/uas/search/console/controller/SearchController.java
  3. 24 0
      search-console/src/main/java/com/uas/search/console/dao/BrandSimpleInfoDao.java
  4. 42 0
      search-console/src/main/java/com/uas/search/console/dao/ComponentSimpleInfoDao.java
  5. 24 0
      search-console/src/main/java/com/uas/search/console/dao/KindSimpleInfoDao.java
  6. 125 0
      search-console/src/main/java/com/uas/search/console/jms/AQListener.java
  7. 151 0
      search-console/src/main/java/com/uas/search/console/jms/QueueMessageParser.java
  8. 56 0
      search-console/src/main/java/com/uas/search/console/jms/QueueMessageTypeFactory.java
  9. 73 0
      search-console/src/main/java/com/uas/search/console/model/BrandSimpleInfo.java
  10. 87 0
      search-console/src/main/java/com/uas/search/console/model/ComponentSimpleInfo.java
  11. 58 0
      search-console/src/main/java/com/uas/search/console/model/KindSimpleInfo.java
  12. 84 0
      search-console/src/main/java/com/uas/search/console/model/ParsedQueueMessage.java
  13. 27 0
      search-console/src/main/java/com/uas/search/console/service/IndexService.java
  14. 165 39
      search-console/src/main/java/com/uas/search/console/service/impl/IndexServiceImpl.java
  15. 141 136
      search-console/src/main/java/com/uas/search/console/service/impl/SearchServiceImpl.java
  16. 20 0
      search-console/src/main/java/com/uas/search/console/util/SearchConstants.java

+ 51 - 0
search-console/src/main/java/com/uas/search/console/controller/IndexController.java

@@ -0,0 +1,51 @@
+package com.uas.search.console.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import com.uas.search.console.jms.AQListener;
+import com.uas.search.console.service.IndexService;
+
+/**
+ * 索引创建相关请求
+ * @author sunyj
+ * @since 2016年8月5日 上午11:42:54
+ */
+@Controller
+@RequestMapping("/index")
+public class IndexController {
+	
+	@Autowired
+	private IndexService indexService;
+	
+	@Autowired
+	private AQListener aqListener;
+	
+	/**
+	 * 初始化创建索引文件
+	 * 
+	 * @return 所用时间 ms
+	 */
+	@RequestMapping("/create")
+	@ResponseBody
+	public String initIndexes() {
+		return "Indexes created success in " + indexService.createIndexs() + " ms.";
+	}
+	
+	/**
+	 * 实时更新索引
+	 * @return
+	 */
+	@RequestMapping("/listen")
+	@ResponseBody
+	public String listen(){
+		new Thread(){
+			public void run() {
+				aqListener.execute();
+			};
+		}.start();
+		return "Listen...";
+	}
+}

+ 0 - 15
search-console/src/main/java/com/uas/search/console/controller/SearchController.java

@@ -9,7 +9,6 @@ import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.ResponseBody;
 
-import com.uas.search.console.service.IndexService;
 import com.uas.search.model.PageParams;
 import com.uas.search.service.SearchService;
 
@@ -26,9 +25,6 @@ public class SearchController {
 	@Autowired
 	private SearchService searchService;
 
-	@Autowired
-	private IndexService indexerService;
-
 	/**
 	 * 搜索产品类目
 	 * 
@@ -129,15 +125,4 @@ public class SearchController {
 		return searchService.getBrandsBySearchComponent(keyword, kindId);
 	}
 
-	/**
-	 * 初始化创建索引文件
-	 * 
-	 * @return 所用时间 ms
-	 */
-	@RequestMapping("/init")
-	@ResponseBody
-	public String initIndexes() {
-		return "Indexes created success in " + indexerService.createIndexs() + " ms.";
-	}
-
 }

+ 24 - 0
search-console/src/main/java/com/uas/search/console/dao/BrandSimpleInfoDao.java

@@ -0,0 +1,24 @@
+package com.uas.search.console.dao;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.stereotype.Repository;
+
+import com.uas.search.console.model.BrandSimpleInfo;
+
+/**
+ * @author sunyj
+ * @since 2016年7月7日 下午5:52:52
+ */
+@Repository
+public interface BrandSimpleInfoDao
+		extends JpaSpecificationExecutor<BrandSimpleInfo>, JpaRepository<BrandSimpleInfo, Long> {
+
+	/**
+	 * 根据id获取品牌
+	 * 
+	 * @param id
+	 * @return
+	 */
+	public BrandSimpleInfo findById(Long id);
+}

+ 42 - 0
search-console/src/main/java/com/uas/search/console/dao/ComponentSimpleInfoDao.java

@@ -0,0 +1,42 @@
+package com.uas.search.console.dao;
+
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.stereotype.Repository;
+
+import com.uas.search.console.model.ComponentSimpleInfo;
+
+/**
+ * @author sunyj
+ * @since 2016年7月7日 下午5:59:24
+ */
+@Repository
+public interface ComponentSimpleInfoDao
+		extends JpaSpecificationExecutor<ComponentSimpleInfo>, JpaRepository<ComponentSimpleInfo, Long> {
+
+	/**
+	 * 根据id获取器件
+	 * 
+	 * @param id
+	 * @return
+	 */
+	public ComponentSimpleInfo findById(Long id);
+
+	/**
+	 * 根据类目kindid获取器件
+	 * 
+	 * @param kindid
+	 * @return
+	 */
+	public List<ComponentSimpleInfo> findByKindid(Long kindid);
+
+	/**
+	 * 根据品牌brandid获取器件
+	 * 
+	 * @param brandid
+	 * @return
+	 */
+	public List<ComponentSimpleInfo> findByBrandid(Long brandid);
+}

+ 24 - 0
search-console/src/main/java/com/uas/search/console/dao/KindSimpleInfoDao.java

@@ -0,0 +1,24 @@
+package com.uas.search.console.dao;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.stereotype.Repository;
+
+import com.uas.search.console.model.KindSimpleInfo;
+
+/**
+ * @author sunyj
+ * @since 2016年7月7日 下午5:46:35
+ */
+@Repository
+public interface KindSimpleInfoDao
+		extends JpaSpecificationExecutor<KindSimpleInfo>, JpaRepository<KindSimpleInfo, Long> {
+
+	/**
+	 * 根据id获取类目
+	 * 
+	 * @param id
+	 * @return
+	 */
+	public KindSimpleInfo findById(Long id);
+}

+ 125 - 0
search-console/src/main/java/com/uas/search/console/jms/AQListener.java

@@ -0,0 +1,125 @@
+package com.uas.search.console.jms;
+
+import java.sql.SQLException;
+import java.util.Properties;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.Session;
+
+import oracle.jms.AQjmsAdtMessage;
+import oracle.jms.AQjmsFactory;
+import oracle.jms.AQjmsSession;
+
+import org.apache.commons.dbcp.BasicDataSource;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.alibaba.fastjson.JSONException;
+import com.uas.search.console.core.util.ContextUtils;
+import com.uas.search.console.model.ParsedQueueMessage;
+import com.uas.search.console.service.IndexService;
+import com.uas.search.console.util.SearchConstants;
+
+/**
+ * 对数据库的消息队列进行实时监听
+ * 
+ * @author sunyj
+ * @since 2016年7月7日 下午8:49:26
+ */
+@Service
+public class AQListener {
+
+	@Autowired
+	private IndexService indexService;
+
+	@Autowired
+	private QueueMessageParser queueMessageParser;
+
+	/**
+	 * 标志,判断是否已存在监听线程,防止重复开启监听线程
+	 */
+	private boolean listening = false;
+
+	public void execute() {
+		if (listening) {
+			System.out.println("已存在线程正在实时监听!");
+			return;
+		}
+		listening = true;
+
+		System.out.println("AQListener started...\n");
+		BasicDataSource dataSource = ContextUtils.getApplicationContext().getBean("dataSource",
+				org.apache.commons.dbcp.BasicDataSource.class);
+
+		try {
+			QueueConnectionFactory queueConnectionFactory = AQjmsFactory.getQueueConnectionFactory(dataSource.getUrl(),
+					new Properties());
+			QueueConnection connection = queueConnectionFactory.createQueueConnection(dataSource.getUsername(),
+					dataSource.getPassword());
+			AQjmsSession session = (AQjmsSession) connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+			connection.start();
+
+			Queue queue = session.getQueue(dataSource.getUsername(), SearchConstants.LUCENE_QUEUE_NAME);
+			MessageConsumer consumer = session.createConsumer(queue, null, QueueMessageTypeFactory.getFactory(), null,
+					false);
+
+			// 添加监听器,队列中一旦有消息入队,就会接受该消息(并不是真的实时,一般会有10秒以内的延迟)
+			consumer.setMessageListener(new MessageListener() {
+				public void onMessage(Message message) {
+					AQjmsAdtMessage adtMessage = (AQjmsAdtMessage) message;
+					try {
+						QueueMessageTypeFactory payload = (QueueMessageTypeFactory) adtMessage.getAdtPayload();
+						// 对出队的消息进行解析、处理
+						process(payload.getMessage());
+					} catch (JMSException e) {
+						e.printStackTrace();
+					} catch (SQLException e) {
+						e.printStackTrace();
+					}
+				}
+			});
+		} catch (JMSException e) {
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * 对得到的队列消息进行解析,之后根据解析出来的对象,对lucene索引进行添加、更新或删除操作
+	 * 
+	 * @param message
+	 */
+	private void process(String message) {
+		ParsedQueueMessage parsedQueueMessage = null;
+		try {
+			parsedQueueMessage = queueMessageParser.parse(message);
+		} catch (JSONException e) {
+			e.printStackTrace();
+		}
+
+		if (parsedQueueMessage == null) {
+			SearchConstants.logger.error("message parsing failed!");
+			return;
+		}
+
+		// 新增索引
+		if (parsedQueueMessage.isInsert()) {
+			indexService.save(parsedQueueMessage.getObject());
+		}
+		// 更新索引
+		else if (parsedQueueMessage.isUpdate()) {
+			indexService.update(parsedQueueMessage.getObject());
+		}
+		// 删除索引
+		else if (parsedQueueMessage.isDelete()) {
+			indexService.delete(parsedQueueMessage.getObject());
+		} else {
+			SearchConstants.logger.error("message parsing failed!");
+		}
+	}
+}

+ 151 - 0
search-console/src/main/java/com/uas/search/console/jms/QueueMessageParser.java

@@ -0,0 +1,151 @@
+package com.uas.search.console.jms;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.alibaba.fastjson.JSONException;
+import com.alibaba.fastjson.JSONObject;
+import com.uas.search.console.core.util.FastjsonUtils;
+import com.uas.search.console.dao.BrandSimpleInfoDao;
+import com.uas.search.console.dao.ComponentSimpleInfoDao;
+import com.uas.search.console.dao.KindSimpleInfoDao;
+import com.uas.search.console.model.BrandSimpleInfo;
+import com.uas.search.console.model.ComponentSimpleInfo;
+import com.uas.search.console.model.KindSimpleInfo;
+import com.uas.search.console.model.ParsedQueueMessage;
+import com.uas.search.console.util.SearchConstants;
+
+
+/**
+ * 对得到的队列消息进行解析的工具
+ * 
+ * @author sunyj
+ * @since 2016年7月7日 下午6:14:03
+ */
+@Service
+public class QueueMessageParser {
+
+	@Autowired
+	private KindSimpleInfoDao kindDao;
+
+	@Autowired
+	private BrandSimpleInfoDao brandDao;
+
+	@Autowired
+	private ComponentSimpleInfoDao componentDao;
+
+	/**
+	 * 对得到的json消息进行解析
+	 * 
+	 * @param message
+	 * @return ParsedQueueMessage对象
+	 * @throws JSONException
+	 */
+	// {"method":"value1","table":"value2","param1":"value3","param2":"value4"}
+	public ParsedQueueMessage parse(String message) throws JSONException {
+		if (StringUtils.isEmpty(message) || message.equals("{}")) {
+			return null;
+		}
+		ParsedQueueMessage parsedQueueMessage = new ParsedQueueMessage();
+		JSONObject jsonObject = FastjsonUtils.parseObject(message);
+
+		// 解析数据库表的更改类型
+		String method = jsonObject.getString("method");
+		if (method.equals("insert")) {
+			parsedQueueMessage.setMethodType(ParsedQueueMessage.INSERT);
+		}
+
+		else if (method.equals("update")) {
+			parsedQueueMessage.setMethodType(ParsedQueueMessage.UPDATE);
+		}
+
+		else if (method.equals("delete")) {
+			parsedQueueMessage.setMethodType(ParsedQueueMessage.DELETE);
+		} else {
+			return null;
+		}
+
+		// 解析哪个表有更改
+		Object object = null;
+		String table = jsonObject.getString("table");
+		if (table.equals(SearchConstants.KIND_TABLE_NAME)) {
+			object = parseKind(jsonObject);
+		} else if (table.equals(SearchConstants.BRAND_TABLE_NAME)) {
+			object = parseBrand(jsonObject);
+		} else if (table.equals(SearchConstants.COMPONENT_TABLE_NAME)) {
+			object = parseComponent(jsonObject);
+		} else {
+			return null;
+		}
+
+		if (object == null) {
+			return null;
+		}
+
+		parsedQueueMessage.setObject(object);
+		return parsedQueueMessage;
+	}
+
+	/**
+	 * 对kind类目进行解析
+	 * 
+	 * @param jsonObject
+	 * @return kind类目对象
+	 * @throws JSONException
+	 */
+	// {"method":"value1","table":"product$kind","ki_id":5}
+	private KindSimpleInfo parseKind(JSONObject jsonObject) throws JSONException {
+		KindSimpleInfo kind = new KindSimpleInfo();
+		Long kindid = jsonObject.getLong("ki_id");
+		kind.setId(kindid);
+
+		KindSimpleInfo temp = kindDao.findById(kindid);
+		// 如果更改是删除的话,根据id获取到的对象为null
+		if (temp != null) {
+			kind = temp;
+		}
+		return kind;
+	}
+
+	/**
+	 * 对brand品牌进行解析
+	 * 
+	 * @param jsonObject
+	 * @return brand品牌对象
+	 * @throws JSONException
+	 */
+	// {"method":"value1","table":"product$brand","br_id":60}
+	private BrandSimpleInfo parseBrand(JSONObject jsonObject) throws JSONException {
+		BrandSimpleInfo brand = new BrandSimpleInfo();
+		Long brandid = jsonObject.getLong("br_id");
+		brand.setId(brandid);
+
+		BrandSimpleInfo temp = brandDao.findById(brandid);
+		if (temp != null) {
+			brand = temp;
+		}
+		return brand;
+	}
+
+	/**
+	 * 对component器件进行解析
+	 * 
+	 * @param jsonObject
+	 * @return component器件对象
+	 * @throws JSONException
+	 */
+	// {"method":"value1","table":"product$component","cmp_id":2029}
+	private ComponentSimpleInfo parseComponent(JSONObject jsonObject) throws JSONException {
+		ComponentSimpleInfo component = new ComponentSimpleInfo();
+		Long componentid = jsonObject.getLong("cmp_id");
+		component.setId(componentid);
+
+		ComponentSimpleInfo temp = componentDao.findById(componentid);
+		if (temp != null) {
+			component = temp;
+		}
+		return component;
+	}
+
+}

+ 56 - 0
search-console/src/main/java/com/uas/search/console/jms/QueueMessageTypeFactory.java

@@ -0,0 +1,56 @@
+package com.uas.search.console.jms;
+
+import java.sql.SQLException;
+
+import oracle.jdbc.OracleTypes;
+import oracle.jdbc.driver.OracleConnection;
+import oracle.jpub.runtime.MutableStruct;
+import oracle.sql.CustomDatum;
+import oracle.sql.CustomDatumFactory;
+import oracle.sql.Datum;
+import oracle.sql.STRUCT;
+
+/**
+ * 对数据库的数据格式进行转换
+ * 
+ * @author sunyj
+ * @since 2016年7月7日 下午8:49:44
+ */
+@SuppressWarnings("deprecation")
+public class QueueMessageTypeFactory implements CustomDatum, CustomDatumFactory {
+
+	public static final int SQL_TYPECODE = OracleTypes.STRUCT;
+
+	MutableStruct struct;
+
+	// 12表示字符串
+	static int[] sqlType = { 12 };
+	static CustomDatumFactory[] factory = new CustomDatumFactory[1];
+	static final QueueMessageTypeFactory messageFactory = new QueueMessageTypeFactory();
+
+	public QueueMessageTypeFactory() {
+		struct = new MutableStruct(new Object[1], sqlType, factory);
+	}
+
+	public static CustomDatumFactory getFactory() {
+		return messageFactory;
+	}
+
+	public CustomDatum create(Datum datum, int sqlType) throws SQLException {
+		if (datum == null) {
+			return null;
+		}
+		QueueMessageTypeFactory queueMessageType = new QueueMessageTypeFactory();
+		queueMessageType.struct = new MutableStruct((STRUCT) datum, QueueMessageTypeFactory.sqlType, factory);
+		return queueMessageType;
+	}
+
+	public Datum toDatum(OracleConnection connection) throws SQLException {
+		return struct.toDatum(connection, "QueueMessageTypeFactory");
+	}
+
+	public String getMessage() throws SQLException {
+		return (String) struct.getAttribute(0);
+	}
+
+}

+ 73 - 0
search-console/src/main/java/com/uas/search/console/model/BrandSimpleInfo.java

@@ -0,0 +1,73 @@
+package com.uas.search.console.model;
+
+import java.io.Serializable;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+/**
+ * 品牌简要信息,只用于索引的创建与查询
+ * 
+ * @author sunyj
+ * @since 2016年7月7日 下午5:48:32
+ */
+@Entity
+@Table(name = "product$brand")
+public class BrandSimpleInfo implements Serializable {
+
+	/**
+	 * 序列号
+	 */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * id
+	 */
+	@Id
+	@Column(name = "br_id")
+	private Long id;
+
+	/**
+	 * uuid
+	 */
+	@Column(name = "br_uuid", unique = true)
+	private String uuid;
+
+	/**
+	 * 品牌中文名称
+	 */
+	@Column(name = "br_name_cn")
+	private String nameCn;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public String getUuid() {
+		return uuid;
+	}
+
+	public void setUuid(String uuid) {
+		this.uuid = uuid;
+	}
+
+	public String getNameCn() {
+		return nameCn;
+	}
+
+	public void setNameCn(String nameCn) {
+		this.nameCn = nameCn;
+	}
+
+	@Override
+	public String toString() {
+		return "BrandSimpleInfo [id=" + id + ", uuid=" + uuid + ", nameCn=" + nameCn + "]";
+	}
+
+}

+ 87 - 0
search-console/src/main/java/com/uas/search/console/model/ComponentSimpleInfo.java

@@ -0,0 +1,87 @@
+package com.uas.search.console.model;
+
+import java.io.Serializable;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+/**
+ * 器件简要信息,只用于索引的创建与查询
+ * 
+ * @author sunyj
+ * @since 2016年7月7日 下午5:55:26
+ */
+@Entity
+@Table(name = "product$component")
+public class ComponentSimpleInfo implements Serializable {
+
+	/**
+	 * 序列号
+	 */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * id
+	 */
+	@Id
+	@Column(name = "cmp_id")
+	private Long id;
+
+	/**
+	 * 原厂型号
+	 */
+	@Column(name = "cmp_code")
+	private String code;
+
+	/**
+	 * 类目id
+	 */
+	@Column(name = "cmp_kiid")
+	private Long kindid;
+
+	/**
+	 * 品牌id
+	 */
+	@Column(name = "cmp_brid")
+	private Long brandid;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public String getCode() {
+		return code;
+	}
+
+	public void setCode(String code) {
+		this.code = code;
+	}
+
+	public Long getKindid() {
+		return kindid;
+	}
+
+	public void setKindid(Long kindid) {
+		this.kindid = kindid;
+	}
+
+	public Long getBrandid() {
+		return brandid;
+	}
+
+	public void setBrandid(Long brandid) {
+		this.brandid = brandid;
+	}
+
+	@Override
+	public String toString() {
+		return "ComponentSimpleInfo [id=" + id + ", code=" + code + ", kindid=" + kindid + ", brandid=" + brandid + "]";
+	}
+
+}

+ 58 - 0
search-console/src/main/java/com/uas/search/console/model/KindSimpleInfo.java

@@ -0,0 +1,58 @@
+package com.uas.search.console.model;
+
+import java.io.Serializable;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+/**
+ * 类目简要信息,只用于索引的创建与查询
+ * 
+ * @author sunyj
+ * @since 2016年7月7日 下午5:38:44
+ */
+@Entity
+@Table(name = "product$kind")
+public class KindSimpleInfo implements Serializable {
+
+	/**
+	 * 序列号
+	 */
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * id
+	 */
+	@Id
+	@Column(name = "ki_id")
+	private Long id;
+
+	/**
+	 * 类目名称
+	 */
+	@Column(name = "ki_name")
+	private String nameCn;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public String getNameCn() {
+		return nameCn;
+	}
+
+	public void setNameCn(String nameCn) {
+		this.nameCn = nameCn;
+	}
+
+	@Override
+	public String toString() {
+		return "KindSimpleInfo [id=" + id + ", nameCn=" + nameCn + "]";
+	}
+}

+ 84 - 0
search-console/src/main/java/com/uas/search/console/model/ParsedQueueMessage.java

@@ -0,0 +1,84 @@
+package com.uas.search.console.model;
+
+/**
+ * 对数据库队列里的消息进行解析后所得到的数据
+ * 
+ * @author sunyj
+ * @since 2016年7月7日 下午8:50:13
+ */
+public class ParsedQueueMessage {
+
+	/**
+	 * 数据库中表的改动为insert类型
+	 */
+	public static final int INSERT = 1;
+
+	/**
+	 * 改动为update类型
+	 */
+	public static final int UPDATE = 2;
+
+	/**
+	 * 改动为delete类型
+	 */
+	public static final int DELETE = 3;
+
+	/**
+	 * 表改动后,解析消息时,更改的类型
+	 */
+	private int methodType;
+
+	/**
+	 * 存放解析出来的对象:kind、brand或component对象
+	 */
+	private Object object;
+
+	/**
+	 * 是否为insert类型
+	 * 
+	 * @return
+	 */
+	public boolean isInsert() {
+		return methodType == INSERT;
+	}
+
+	/**
+	 * 是否为update类型
+	 * 
+	 * @return
+	 */
+	public boolean isUpdate() {
+		return methodType == UPDATE;
+	}
+
+	/**
+	 * 是否为delete类型
+	 * 
+	 * @return
+	 */
+	public boolean isDelete() {
+		return methodType == DELETE;
+	}
+
+	public int getMethodType() {
+		return methodType;
+	}
+
+	public void setMethodType(int methodType) {
+		this.methodType = methodType;
+	}
+
+	public Object getObject() {
+		return object;
+	}
+
+	public void setObject(Object object) {
+		this.object = object;
+	}
+
+	@Override
+	public String toString() {
+		return "ParsedQueueMessage [methodType=" + methodType + ", object=" + object + "]";
+	}
+
+}

+ 27 - 0
search-console/src/main/java/com/uas/search/console/service/IndexService.java

@@ -2,6 +2,9 @@ package com.uas.search.console.service;
 
 /**
  * 索引处理
+ * 
+ * @author sunyj
+ * @since 2016年8月5日 下午2:33:58
  */
 public interface IndexService {
 
@@ -12,4 +15,28 @@ public interface IndexService {
 	 */
 	public Long createIndexs();
 
+	/**
+	 * 将新对象添加在lucene索引中
+	 * 
+	 * @param obj
+	 *            类目、品牌或器件对象
+	 */
+	public void save(Object obj);
+
+	/**
+	 * 根据新对象对lucene索引进行更新
+	 * 
+	 * @param obj
+	 *            类目、品牌或器件对象
+	 */
+	public void update(Object obj);
+
+	/**
+	 * 将对象从lucene索引中删除
+	 * 
+	 * @param obj
+	 *            类目、品牌或器件对象
+	 */
+	public void delete(Object obj);
+
 }

+ 165 - 39
search-console/src/main/java/com/uas/search/console/service/impl/IndexServiceImpl.java

@@ -11,6 +11,7 @@ import org.apache.lucene.document.Field.Store;
 import org.apache.lucene.document.StringField;
 import org.apache.lucene.document.TextField;
 import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.Term;
 import org.apache.lucene.store.FSDirectory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.Page;
@@ -20,47 +21,61 @@ import org.springframework.util.StringUtils;
 
 import com.uas.platform.core.model.PageInfo;
 import com.uas.platform.core.model.PageParams;
-import com.uas.search.console.dao.BrandInfoDao;
-import com.uas.search.console.dao.ComponentInfoDao;
-import com.uas.search.console.dao.KindInfoDao;
-import com.uas.search.console.model.BrandInfo;
-import com.uas.search.console.model.ComponentInfo;
-import com.uas.search.console.model.KindInfo;
+import com.uas.search.console.dao.BrandSimpleInfoDao;
+import com.uas.search.console.dao.ComponentSimpleInfoDao;
+import com.uas.search.console.dao.KindSimpleInfoDao;
+import com.uas.search.console.model.BrandSimpleInfo;
+import com.uas.search.console.model.ComponentSimpleInfo;
+import com.uas.search.console.model.KindSimpleInfo;
 import com.uas.search.console.service.IndexService;
 import com.uas.search.console.support.IndexWriterManager;
 import com.uas.search.console.util.SearchConstants;
 
 /**
  * 创建索引
+ * 
+ * @author sunyj
+ * @since 2016年8月5日 下午2:23:22
  */
 @Service
 public class IndexServiceImpl implements IndexService {
 
 	@Autowired
-	private KindInfoDao kindDao;
+	private KindSimpleInfoDao kindDao;
 
 	@Autowired
-	private BrandInfoDao brandDao;
+	private BrandSimpleInfoDao brandDao;
 
 	@Autowired
-	private ComponentInfoDao componentDao;
+	private ComponentSimpleInfoDao componentDao;
 
 	private IndexWriter indexWriter;
 
 	private static IndexWriterManager indexWriterManager;
 
+	private FSDirectory directory;
+
 	private static final int PAGE_SIZE = 1000;
 
 	public IndexServiceImpl() {
 		try {
-			FSDirectory fsDirectory = FSDirectory.open(Paths.get(SearchConstants.INDEX_DIR));
-			indexWriterManager = new IndexWriterManager(fsDirectory);
+			directory = FSDirectory.open(Paths.get(SearchConstants.INDEX_DIR));
+			indexWriterManager = new IndexWriterManager(directory);
 		} catch (IOException e) {
 			e.printStackTrace();
 		}
 	}
 
 	public Long createIndexs() {
+		try {
+			if (IndexWriter.isLocked(directory)) {
+				System.out.println("已有线程正在创建索引!");
+				return 0L;
+			}
+		} catch (IOException e1) {
+			e1.printStackTrace();
+		}
+
 		// 清除旧索引
 		File file = new File(SearchConstants.INDEX_DIR);
 		if (file.isDirectory()) {
@@ -73,15 +88,15 @@ public class IndexServiceImpl implements IndexService {
 
 			Long kindSize = createKindIndexs();
 			Long kindTime = new Date().getTime();
-			System.out.println("创建类目索引: " + kindSize + "条,耗时 " + (kindTime - startTime) + " ms");
+			System.out.println("创建类目索引: " + kindSize + "条,耗时 " + (kindTime - startTime) + " ms\n");
 
 			Long brandSize = createBrandIndexs();
 			Long brandTime = new Date().getTime();
-			System.out.println("创建品牌索引: " + brandSize + "条,耗时 " + (brandTime - kindTime) + " ms");
+			System.out.println("创建品牌索引: " + brandSize + "条,耗时 " + (brandTime - kindTime) + " ms\n");
 
 			Long componentSize = createComponentIndexs();
 			Long componentTime = new Date().getTime();
-			System.out.println("创建器件索引: " + componentSize + "条,耗时 " + (componentTime - brandTime) + " ms");
+			System.out.println("创建器件索引: " + componentSize + "条,耗时 " + (componentTime - brandTime) + " ms\n");
 
 			System.out.println("索引创建成功, 共用时间 " + (componentTime - startTime) + " ms");
 
@@ -104,12 +119,13 @@ public class IndexServiceImpl implements IndexService {
 	 * @throws IOException
 	 */
 	private Long createKindIndexs() throws IOException {
-		List<KindInfo> kinds = kindDao.findAll();
+		System.out.println("正在创建类目索引...");
+		List<KindSimpleInfo> kinds = kindDao.findAll();
 
 		if (CollectionUtils.isEmpty(kinds))
 			return 0L;
 
-		for (KindInfo kind : kinds) {
+		for (KindSimpleInfo kind : kinds) {
 			Document document = toDocument(kind);
 			if (document != null)
 				indexWriter.addDocument(document);
@@ -125,11 +141,12 @@ public class IndexServiceImpl implements IndexService {
 	 * @throws IOException
 	 */
 	private Long createBrandIndexs() throws IOException {
-		List<BrandInfo> brands = brandDao.findAll();
+		System.out.println("正在创建品牌索引...");
+		List<BrandSimpleInfo> brands = brandDao.findAll();
 		if (CollectionUtils.isEmpty(brands))
 			return 0L;
 
-		for (BrandInfo brand : brands) {
+		for (BrandSimpleInfo brand : brands) {
 			Document document = toDocument(brand);
 			if (document != null) {
 				indexWriter.addDocument(document);
@@ -146,6 +163,7 @@ public class IndexServiceImpl implements IndexService {
 	 * @throws IOException
 	 */
 	public Long createComponentIndexs() throws IOException {
+		System.out.println("正在创建器件索引...");
 		Long size = 0L;
 		PageParams params = new PageParams();
 		int page = 1;
@@ -153,10 +171,10 @@ public class IndexServiceImpl implements IndexService {
 		params.setPage(page);
 
 		PageInfo info = new PageInfo(params);
-		Page<ComponentInfo> pageResult = componentDao.findAll(info);
+		Page<ComponentSimpleInfo> pageResult = componentDao.findAll(info);
 		while (pageResult.getTotalElements() > size) {
-			List<ComponentInfo> components = pageResult.getContent();
-			for (ComponentInfo component : components) {
+			List<ComponentSimpleInfo> components = pageResult.getContent();
+			for (ComponentSimpleInfo component : components) {
 				Document document = toDocument(component);
 				if (document != null) {
 					indexWriter.addDocument(document);
@@ -179,7 +197,7 @@ public class IndexServiceImpl implements IndexService {
 	 * @param kind
 	 * @return
 	 */
-	private Document toDocument(KindInfo kind) {
+	private Document toDocument(KindSimpleInfo kind) {
 		Document document = new Document();
 		// 不能用LongField,否则后续实时更新索引时,方法updateDocument(new Term("", ""),
 		// doc)无法根据id进行更新
@@ -197,7 +215,7 @@ public class IndexServiceImpl implements IndexService {
 	 * @param brand
 	 * @return
 	 */
-	private Document toDocument(BrandInfo brand) {
+	private Document toDocument(BrandSimpleInfo brand) {
 		Document document = new Document();
 		if (brand == null || brand.getId() == null || StringUtils.isEmpty(brand.getNameCn())) {
 			return null;
@@ -215,7 +233,7 @@ public class IndexServiceImpl implements IndexService {
 	 * @param component
 	 * @return
 	 */
-	private Document toDocument(ComponentInfo component) {
+	private Document toDocument(ComponentSimpleInfo component) {
 		if (component == null || component.getId() == null || StringUtils.isEmpty(component.getCode())) {
 			return null;
 		}
@@ -226,23 +244,35 @@ public class IndexServiceImpl implements IndexService {
 		String code = component.getCode().toLowerCase();
 		document.add(new StringField(SearchConstants.COMPONENT_CODE_FIELD, code, Store.YES));
 
-		if (component.getKind() != null) {
-			// 不用LongField
-			document.add(new StringField(SearchConstants.COMPONENT_KINDID_FIELD, String.valueOf(component.getKind()
-					.getId()), Store.YES));
-			document.add(new StringField(SearchConstants.COMPONENT_KINDNAME_FIELD, component.getKind().getNameCn(),
-					Store.YES));
+		Long kindid = component.getKindid();
+		if (kindid != null) {
+			document.add(new StringField(SearchConstants.COMPONENT_KINDID_FIELD, String.valueOf(kindid), Store.YES));
 		}
-
-		if (component.getBrand() != null) {
-			// 不用LongField
-			document.add(new StringField(SearchConstants.COMPONENT_BRANDID_FIELD, String.valueOf(component.getBrand()
-					.getId()), Store.YES));
-			document.add(new StringField(SearchConstants.COMPONENT_BRANDUUID_FIELD, String.valueOf(component.getBrand()
-					.getUuid()), Store.YES));
-			document.add(new StringField(SearchConstants.COMPONENT_BRANDNAME_FIELD, String.valueOf(component.getBrand()
-					.getNameCn()), Store.YES));
+		// if (component.getKind() != null) {
+		// 不用LongField
+		// document.add(new StringField(SearchConstants.COMPONENT_KINDID_FIELD,
+		// String.valueOf(component.getKind().getId()), Store.YES));
+		// document.add(new
+		// StringField(SearchConstants.COMPONENT_KINDNAME_FIELD,
+		// component.getKind().getNameCn(),
+		// Store.YES));
+		// }
+
+		Long brandid = component.getBrandid();
+		if (brandid != null) {
+			document.add(new StringField(SearchConstants.COMPONENT_BRANDID_FIELD, String.valueOf(brandid), Store.YES));
 		}
+		// if (component.getBrand() != null) {
+		// 不用LongField
+		// document.add(new StringField(SearchConstants.COMPONENT_BRANDID_FIELD,
+		// String.valueOf(component.getBrand().getId()), Store.YES));
+		// document.add(new
+		// StringField(SearchConstants.COMPONENT_BRANDUUID_FIELD,
+		// String.valueOf(component.getBrand().getUuid()), Store.YES));
+		// document.add(new
+		// StringField(SearchConstants.COMPONENT_BRANDNAME_FIELD,
+		// String.valueOf(component.getBrand().getNameCn()), Store.YES));
+		// }
 
 		// TODO 属性值加入索引
 		return document;
@@ -272,4 +302,100 @@ public class IndexServiceImpl implements IndexService {
 		}
 	}
 
+	public void save(Object obj) {
+		if (obj == null) {
+			return;
+		}
+
+		try {
+			indexWriter = indexWriterManager.get();
+			if (obj instanceof KindSimpleInfo) {
+				indexWriter.addDocument(toDocument((KindSimpleInfo) obj));
+			} else if (obj instanceof BrandSimpleInfo) {
+				indexWriter.addDocument(toDocument((BrandSimpleInfo) obj));
+			} else if (obj instanceof ComponentSimpleInfo) {
+				indexWriter.addDocument(toDocument((ComponentSimpleInfo) obj));
+			} else {
+				SearchConstants.logger.error("message parsing failed!");
+			}
+			indexWriter.commit();
+		} catch (IOException e) {
+			e.printStackTrace();
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+		} finally {
+			indexWriterManager.release();
+		}
+		System.out.println("saved object... " + obj + "\n");
+	}
+
+	public void update(Object obj) {
+		if (obj == null) {
+			return;
+		}
+		try {
+			indexWriter = indexWriterManager.get();
+			if (obj instanceof KindSimpleInfo) {
+				KindSimpleInfo kind = (KindSimpleInfo) obj;
+				Long kindid = kind.getId();
+				if (kindid == null) {
+					return;
+				}
+				indexWriter.updateDocument(new Term(SearchConstants.KIND_ID_FIELD, String.valueOf(kindid)),
+						toDocument(kind));
+			} else if (obj instanceof BrandSimpleInfo) {
+				BrandSimpleInfo brand = (BrandSimpleInfo) obj;
+				Long brandid = brand.getId();
+				if (brandid == null) {
+					return;
+				}
+				indexWriter.updateDocument(new Term(SearchConstants.BRAND_ID_FIELD, String.valueOf(brandid)),
+						toDocument(brand));
+			} else if (obj instanceof ComponentSimpleInfo) {
+				ComponentSimpleInfo component = (ComponentSimpleInfo) obj;
+				indexWriter.updateDocument(
+						new Term(SearchConstants.COMPONENT_ID_FIELD, String.valueOf(component.getId())),
+						toDocument(component));
+			} else {
+				SearchConstants.logger.error("message parsing failed!");
+			}
+			indexWriter.commit();
+		} catch (IOException e) {
+			e.printStackTrace();
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+		} finally {
+			indexWriterManager.release();
+		}
+		System.out.println("updated object... " + obj + "\n");
+	}
+
+	public void delete(Object obj) {
+		if (obj == null) {
+			return;
+		}
+		try {
+			indexWriter = indexWriterManager.get();
+			if (obj instanceof KindSimpleInfo) {
+				indexWriter.deleteDocuments(
+						new Term(SearchConstants.KIND_ID_FIELD, ((KindSimpleInfo) obj).getId().toString()));
+			} else if (obj instanceof BrandSimpleInfo) {
+				indexWriter.deleteDocuments(
+						new Term(SearchConstants.BRAND_ID_FIELD, ((BrandSimpleInfo) obj).getId().toString()));
+			} else if (obj instanceof ComponentSimpleInfo) {
+				indexWriter.deleteDocuments(
+						new Term(SearchConstants.COMPONENT_ID_FIELD, ((ComponentSimpleInfo) obj).getId().toString()));
+			} else {
+				SearchConstants.logger.error("message parsing failed!");
+			}
+			indexWriter.commit();
+		} catch (IOException e) {
+			e.printStackTrace();
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+		} finally {
+			indexWriterManager.release();
+		}
+		System.out.println("deleted object... " + obj + "\n");
+	}
 }

+ 141 - 136
search-console/src/main/java/com/uas/search/console/service/impl/SearchServiceImpl.java

@@ -32,6 +32,12 @@ import com.uas.search.console.util.SearchConstants;
 import com.uas.search.model.PageParams;
 import com.uas.search.service.SearchService;
 
+/**
+ * 搜索索引
+ * 
+ * @author sunyj
+ * @since 2016年8月5日 下午2:21:26
+ */
 @Service
 public class SearchServiceImpl implements SearchService {
 
@@ -82,6 +88,49 @@ public class SearchServiceImpl implements SearchService {
 		return ids;
 	}
 
+	public List<Map<String, Object>> getKinds(String keyword) {
+		if (isKeywordInvalid(keyword)) {
+			throw new IllegalArgumentException("搜索关键词无效");
+		}
+
+		List<Map<String, Object>> kinds = new ArrayList<Map<String, Object>>();
+		searcherManager.maybeReopen();
+		IndexSearcher searcher = searcherManager.get();
+		if (searcher == null) {
+			throw new RuntimeException("获取索引文件失败");
+		}
+
+		BooleanQuery booleanQuery = new BooleanQuery();
+
+		@SuppressWarnings("resource")
+		Analyzer analyzer = new IKAnalyzer(true);
+		TokenStream tokenStream;
+		try {
+			tokenStream = analyzer.tokenStream(SearchConstants.KIND_NAMECN_FIELD, keyword);
+			tokenStream.reset();
+			CharTermAttribute cta = tokenStream.addAttribute(CharTermAttribute.class);
+			while (tokenStream.incrementToken()) {
+				Query query1 = new PrefixQuery(new Term(SearchConstants.KIND_NAMECN_FIELD, cta.toString()));
+				booleanQuery.add(query1, BooleanClause.Occur.MUST);
+			}
+
+			TopDocs hits = searcher.search(booleanQuery, TOP_NUM);
+			ScoreDoc[] scoreDocs = hits.scoreDocs;
+			for (ScoreDoc doc : scoreDocs) {
+				Document document = searcher.doc(doc.doc);
+				Map<String, Object> kind = new HashMap<String, Object>();
+				kind.put("id", Long.parseLong(document.get(SearchConstants.KIND_ID_FIELD)));
+				kind.put("nameCn", document.get(SearchConstants.KIND_NAMECN_FIELD));
+				kinds.add(kind);
+			}
+		} catch (IOException e) {
+			e.printStackTrace();
+		} finally {
+			searcherManager.release(searcher);
+		}
+		return kinds;
+	}
+
 	public List<Long> getBrandIds(String keyword) {
 		if (isKeywordInvalid(keyword)) {
 			throw new IllegalArgumentException("搜索关键词无效");
@@ -122,6 +171,49 @@ public class SearchServiceImpl implements SearchService {
 		return ids;
 	}
 
+	public List<Map<String, Object>> getBrands(String keyword) {
+		if (isKeywordInvalid(keyword)) {
+			throw new IllegalArgumentException("搜索关键词无效");
+		}
+
+		List<Map<String, Object>> brands = new ArrayList<Map<String, Object>>();
+		searcherManager.maybeReopen();
+		IndexSearcher searcher = searcherManager.get();
+		if (searcher == null) {
+			throw new RuntimeException("获取索引文件失败");
+		}
+
+		BooleanQuery booleanQuery = new BooleanQuery();
+
+		@SuppressWarnings("resource")
+		Analyzer analyzer = new IKAnalyzer(true);
+		TokenStream tokenStream;
+		try {
+			tokenStream = analyzer.tokenStream(SearchConstants.BRAND_NAMECN_FIELD, keyword);
+			tokenStream.reset();
+			CharTermAttribute cta = tokenStream.addAttribute(CharTermAttribute.class);
+			while (tokenStream.incrementToken()) {
+				Query query1 = new PrefixQuery(new Term(SearchConstants.BRAND_NAMECN_FIELD, cta.toString()));
+				booleanQuery.add(query1, BooleanClause.Occur.MUST);
+			}
+			TopDocs hits = searcher.search(booleanQuery, TOP_NUM);
+			ScoreDoc[] scoreDocs = hits.scoreDocs;
+			for (ScoreDoc doc : scoreDocs) {
+				Document document = searcher.doc(doc.doc);
+				Map<String, Object> brand = new HashMap<String, Object>();
+				brand.put("id", Long.parseLong(document.get(SearchConstants.BRAND_ID_FIELD)));
+				brand.put("uuid", document.get(SearchConstants.BRAND_UUID_FIELD));
+				brand.put("nameCn", document.get(SearchConstants.BRAND_NAMECN_FIELD));
+				brands.add(brand);
+			}
+		} catch (IOException e) {
+			e.printStackTrace();
+		} finally {
+			searcherManager.release(searcher);
+		}
+		return brands;
+	}
+
 	public Map<String, Object> getComponentIds(String keyword, PageParams page) {
 		if (isKeywordInvalid(keyword)) {
 			throw new IllegalArgumentException("搜索关键词无效");
@@ -232,98 +324,29 @@ public class SearchServiceImpl implements SearchService {
 		return kindIds;
 	}
 
-	public Set<Long> getBrandIdsBySearchComponent(String keyword, String kindId) {
-		if (isKeywordInvalid(keyword)) {
-			throw new IllegalArgumentException("搜索关键词无效");
+	public List<Map<String, Object>> getKindsBySearchComponent(String keyword, String brandId) {
+		Set<Long> kindIds = getKindIdsBySearchComponent(keyword, brandId);
+		List<Map<String, Object>> kinds = new ArrayList<Map<String, Object>>();
+		if (CollectionUtils.isEmpty(kindIds)) {
+			return kinds;
 		}
 
-		Set<Long> brandIds = new HashSet<Long>();
 		searcherManager.maybeReopen();
 		IndexSearcher searcher = searcherManager.get();
 		if (searcher == null) {
 			throw new RuntimeException("获取索引文件失败");
 		}
 
+		BooleanQuery booleanQuery = new BooleanQuery();
+		for (Long kindId : kindIds) {
+			booleanQuery.add(new TermQuery(new Term(SearchConstants.KIND_ID_FIELD, String.valueOf(kindId))),
+					BooleanClause.Occur.SHOULD);
+		}
 		try {
-			BooleanQuery booleanQuery = new BooleanQuery();
-
-			keyword = URLDecoder.decode(keyword, "UTF-8");
-			keyword = keyword.toLowerCase();
-			PrefixQuery prefixQuery = new PrefixQuery(new Term(SearchConstants.COMPONENT_CODE_FIELD, keyword));
-			booleanQuery.add(prefixQuery, BooleanClause.Occur.MUST);
-
-			// 筛选类目
-			if (!StringUtils.isEmpty(kindId)) {
-				TermQuery kindQuery = new TermQuery(new Term(SearchConstants.COMPONENT_KINDID_FIELD, kindId));
-				booleanQuery.add(kindQuery, BooleanClause.Occur.MUST);
-			}
-
-			TopDocs hits = searcher.search(booleanQuery, TOP_NUM);
-			ScoreDoc[] scoreDocs = hits.scoreDocs;
+			TopDocs topDocs = searcher.search(booleanQuery, TOP_NUM);
+			ScoreDoc[] scoreDocs = topDocs.scoreDocs;
 			for (ScoreDoc scoreDoc : scoreDocs) {
 				Document document = searcher.doc(scoreDoc.doc);
-				String cmp_brand_id = document.get(SearchConstants.COMPONENT_BRANDID_FIELD);
-				if (!StringUtils.isEmpty(cmp_brand_id)) {
-					brandIds.add(Long.parseLong(cmp_brand_id));
-				}
-			}
-		} catch (IOException e) {
-			e.printStackTrace();
-		} finally {
-			searcherManager.release(searcher);
-		}
-
-		return brandIds;
-	}
-
-	/**
-	 * 判断搜索词是否为无效的(比如只包含特殊字符,不含有任何字母、数字、汉字等有意义的字符)
-	 * 
-	 * @param keyword
-	 * @return
-	 */
-	private boolean isKeywordInvalid(String keyword) {
-		if (StringUtils.isEmpty(keyword)) {
-			return true;
-		}
-		// 将特殊字符剔除
-		keyword = keyword.replaceAll("[`~!@#$^&*()=|{}':;',\\[\\].<>/?~!@#¥……&*()——|{}【】‘;:”“'。,、?]+", "");
-		if (StringUtils.isEmpty(keyword)) {
-			return true;
-		}
-		return false;
-	}
-
-	public List<Map<String, Object>> getKinds(String keyword) {
-		if (isKeywordInvalid(keyword)) {
-			throw new IllegalArgumentException("搜索关键词无效");
-		}
-
-		List<Map<String, Object>> kinds = new ArrayList<Map<String, Object>>();
-		searcherManager.maybeReopen();
-		IndexSearcher searcher = searcherManager.get();
-		if (searcher == null) {
-			throw new RuntimeException("获取索引文件失败");
-		}
-
-		BooleanQuery booleanQuery = new BooleanQuery();
-
-		@SuppressWarnings("resource")
-		Analyzer analyzer = new IKAnalyzer(true);
-		TokenStream tokenStream;
-		try {
-			tokenStream = analyzer.tokenStream(SearchConstants.KIND_NAMECN_FIELD, keyword);
-			tokenStream.reset();
-			CharTermAttribute cta = tokenStream.addAttribute(CharTermAttribute.class);
-			while (tokenStream.incrementToken()) {
-				Query query1 = new PrefixQuery(new Term(SearchConstants.KIND_NAMECN_FIELD, cta.toString()));
-				booleanQuery.add(query1, BooleanClause.Occur.MUST);
-			}
-
-			TopDocs hits = searcher.search(booleanQuery, TOP_NUM);
-			ScoreDoc[] scoreDocs = hits.scoreDocs;
-			for (ScoreDoc doc : scoreDocs) {
-				Document document = searcher.doc(doc.doc);
 				Map<String, Object> kind = new HashMap<String, Object>();
 				kind.put("id", Long.parseLong(document.get(SearchConstants.KIND_ID_FIELD)));
 				kind.put("nameCn", document.get(SearchConstants.KIND_NAMECN_FIELD));
@@ -334,79 +357,44 @@ public class SearchServiceImpl implements SearchService {
 		} finally {
 			searcherManager.release(searcher);
 		}
+
 		return kinds;
 	}
 
-	public List<Map<String, Object>> getBrands(String keyword) {
+	public Set<Long> getBrandIdsBySearchComponent(String keyword, String kindId) {
 		if (isKeywordInvalid(keyword)) {
 			throw new IllegalArgumentException("搜索关键词无效");
 		}
 
-		List<Map<String, Object>> brands = new ArrayList<Map<String, Object>>();
+		Set<Long> brandIds = new HashSet<Long>();
 		searcherManager.maybeReopen();
 		IndexSearcher searcher = searcherManager.get();
 		if (searcher == null) {
 			throw new RuntimeException("获取索引文件失败");
 		}
 
-		BooleanQuery booleanQuery = new BooleanQuery();
-
-		@SuppressWarnings("resource")
-		Analyzer analyzer = new IKAnalyzer(true);
-		TokenStream tokenStream;
 		try {
-			tokenStream = analyzer.tokenStream(SearchConstants.BRAND_NAMECN_FIELD, keyword);
-			tokenStream.reset();
-			CharTermAttribute cta = tokenStream.addAttribute(CharTermAttribute.class);
-			while (tokenStream.incrementToken()) {
-				Query query1 = new PrefixQuery(new Term(SearchConstants.BRAND_NAMECN_FIELD, cta.toString()));
-				booleanQuery.add(query1, BooleanClause.Occur.MUST);
-			}
-			TopDocs hits = searcher.search(booleanQuery, TOP_NUM);
-			ScoreDoc[] scoreDocs = hits.scoreDocs;
-			for (ScoreDoc doc : scoreDocs) {
-				Document document = searcher.doc(doc.doc);
-				Map<String, Object> brand = new HashMap<String, Object>();
-				brand.put("id", Long.parseLong(document.get(SearchConstants.BRAND_ID_FIELD)));
-				brand.put("uuid", document.get(SearchConstants.BRAND_UUID_FIELD));
-				brand.put("nameCn", document.get(SearchConstants.BRAND_NAMECN_FIELD));
-				brands.add(brand);
-			}
-		} catch (IOException e) {
-			e.printStackTrace();
-		} finally {
-			searcherManager.release(searcher);
-		}
-		return brands;
-	}
+			BooleanQuery booleanQuery = new BooleanQuery();
 
-	public List<Map<String, Object>> getKindsBySearchComponent(String keyword, String brandId) {
-		Set<Long> kindIds = getKindIdsBySearchComponent(keyword, brandId);
-		List<Map<String, Object>> kinds = new ArrayList<Map<String, Object>>();
-		if (CollectionUtils.isEmpty(kindIds)) {
-			return kinds;
-		}
+			keyword = URLDecoder.decode(keyword, "UTF-8");
+			keyword = keyword.toLowerCase();
+			PrefixQuery prefixQuery = new PrefixQuery(new Term(SearchConstants.COMPONENT_CODE_FIELD, keyword));
+			booleanQuery.add(prefixQuery, BooleanClause.Occur.MUST);
 
-		searcherManager.maybeReopen();
-		IndexSearcher searcher = searcherManager.get();
-		if (searcher == null) {
-			throw new RuntimeException("获取索引文件失败");
-		}
+			// 筛选类目
+			if (!StringUtils.isEmpty(kindId)) {
+				TermQuery kindQuery = new TermQuery(new Term(SearchConstants.COMPONENT_KINDID_FIELD, kindId));
+				booleanQuery.add(kindQuery, BooleanClause.Occur.MUST);
+			}
 
-		BooleanQuery booleanQuery = new BooleanQuery();
-		for (Long kindId : kindIds) {
-			booleanQuery.add(new TermQuery(new Term(SearchConstants.KIND_ID_FIELD, String.valueOf(kindId))),
-					BooleanClause.Occur.SHOULD);
-		}
-		try {
-			TopDocs topDocs = searcher.search(booleanQuery, TOP_NUM);
-			ScoreDoc[] scoreDocs = topDocs.scoreDocs;
+			TopDocs hits = searcher.search(booleanQuery, TOP_NUM);
+			ScoreDoc[] scoreDocs = hits.scoreDocs;
 			for (ScoreDoc scoreDoc : scoreDocs) {
 				Document document = searcher.doc(scoreDoc.doc);
-				Map<String, Object> kind = new HashMap<String, Object>();
-				kind.put("id", Long.parseLong(document.get(SearchConstants.KIND_ID_FIELD)));
-				kind.put("nameCn", document.get(SearchConstants.KIND_NAMECN_FIELD));
-				kinds.add(kind);
+				String cmp_brand_id = document.get(SearchConstants.COMPONENT_BRANDID_FIELD);
+				if (!StringUtils.isEmpty(cmp_brand_id)) {
+					brandIds.add(Long.parseLong(cmp_brand_id));
+				}
 			}
 		} catch (IOException e) {
 			e.printStackTrace();
@@ -414,7 +402,7 @@ public class SearchServiceImpl implements SearchService {
 			searcherManager.release(searcher);
 		}
 
-		return kinds;
+		return brandIds;
 	}
 
 	public List<Map<String, Object>> getBrandsBySearchComponent(String keyword, String kindId) {
@@ -455,4 +443,21 @@ public class SearchServiceImpl implements SearchService {
 		return brands;
 	}
 
+	/**
+	 * 判断搜索词是否为无效的(比如只包含特殊字符,不含有任何字母、数字、汉字等有意义的字符)
+	 * 
+	 * @param keyword
+	 * @return
+	 */
+	private boolean isKeywordInvalid(String keyword) {
+		if (StringUtils.isEmpty(keyword)) {
+			return true;
+		}
+		// 将特殊字符剔除
+		keyword = keyword.replaceAll("[`~!@#$^&*()=|{}':;',\\[\\].<>/?~!@#¥……&*()——|{}【】‘;:”“'。,、?]+", "");
+		if (StringUtils.isEmpty(keyword)) {
+			return true;
+		}
+		return false;
+	}
 }

+ 20 - 0
search-console/src/main/java/com/uas/search/console/util/SearchConstants.java

@@ -31,6 +31,26 @@ public class SearchConstants {
 	 */
 	public static final int KEYWORD_MAX_LENGTH = 30;
 
+	/**
+	 * 消息队列名,该队列存放kind、brand、component三个表的改动信息
+	 */
+	public static final String LUCENE_QUEUE_NAME = "lucene_queue";
+
+	/**
+	 * 类目表名
+	 */
+	public static final String KIND_TABLE_NAME = "product$kind";
+
+	/**
+	 * 品牌表名
+	 */
+	public static final String BRAND_TABLE_NAME = "product$brand";
+
+	/**
+	 * 器件表名
+	 */
+	public static final String COMPONENT_TABLE_NAME = "product$component";
+
 	/**
 	 * 索引文件存储路径
 	 */