sunyj 9 жил өмнө
parent
commit
1f0f45dfbc

+ 12 - 3
search-console-b2b/src/main/java/com/uas/search/console/b2b/controller/IndexController.java

@@ -33,13 +33,22 @@ public class IndexController {
 
 	@RequestMapping("/create")
 	@ResponseBody
-	public String createIndexs(Boolean fromFiles) {
-		return "Indexes created success in " + indexService.createIndexs(fromFiles) + " ms.";
+	public String createIndexs(String tableNames, Boolean fromFiles) {
+		if (!StringUtils.isEmpty(tableNames)) {
+			String[] strs = tableNames.split(",");
+			List<Table_name> tableNameList = new ArrayList<>();
+			for (String str : strs) {
+				tableNameList.add(Table_name.valueOf(str.toUpperCase()));
+			}
+			return "Indexes created success in " + indexService.createIndexs(tableNameList, fromFiles) + " ms.";
+		} else {
+			return "Indexes created success in " + indexService.createIndexs(null, fromFiles) + " ms.";
+		}
 	}
 
 	@RequestMapping("/downloadData")
 	@ResponseBody
-	public String createIndexs(String tableNames) {
+	public String downloadData(String tableNames) {
 		if (!StringUtils.isEmpty(tableNames)) {
 			String[] strs = tableNames.split(",");
 			List<Table_name> tableNameList = new ArrayList<>();

+ 6 - 4
search-console-b2b/src/main/java/com/uas/search/console/b2b/service/IndexService.java

@@ -13,19 +13,21 @@ import com.uas.search.b2b.service.SearchService.Table_name;
 public interface IndexService {
 
 	/**
-	 * 创建索引
+	 * 创建指定表的索引
 	 * 
+	 * @param tableNames
+	 *            指定的表,默认创建所有表的索引
 	 * @param fromFiles
-	 *            索引数据是否来自文件
+	 *            索引数据是否来自文件,默认来自文件
 	 * @return 创建的索引花费总时间 ms
 	 */
-	public Long createIndexs(Boolean fromFiles);
+	public Long createIndexs(List<Table_name> tableNames, Boolean fromFiles);
 
 	/**
 	 * 下载指定表的数据至本地文件中,以供建索引用
 	 * 
 	 * @param tableNames
-	 *            指定的表
+	 *            指定的表,默认下载所有表的数据
 	 * @return 花费总时间 ms
 	 */
 	public Long downloadDataFromDatabase(List<Table_name> tableNames);

+ 101 - 99
search-console-b2b/src/main/java/com/uas/search/console/b2b/service/impl/IndexServiceImpl.java

@@ -6,7 +6,6 @@ import java.io.FileNotFoundException;
 import java.io.FileReader;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
@@ -17,7 +16,6 @@ import org.apache.log4j.Logger;
 import org.apache.lucene.document.Document;
 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;
 import org.springframework.data.jpa.repository.JpaRepository;
@@ -46,79 +44,54 @@ import com.uas.search.console.b2b.util.SearchUtils;
 @Service
 public class IndexServiceImpl implements IndexService {
 
+	@Autowired
+	private AQListener aqListener;
+
 	private static final int PAGE_SIZE = 1000;
 
 	// 单个文件存储的最大数据数目
 	public static final int SINGLE_FILE_MAX_SIZE = 100000;
 
-	private IndexWriter indexWriter;
-
-	private static IndexWriterManager indexWriterManager;
-
-	private FSDirectory directory;
-
-	@Autowired
-	private AQListener aqListener;
-
 	/**
 	 * 是否正在创建索引
 	 */
 	private boolean creatingIndex = false;
 
-	Logger logger = Logger.getLogger(getClass());
+	private IndexWriter indexWriter;
 
-	public IndexServiceImpl() {
-		try {
-			directory = FSDirectory.open(Paths.get(SearchConstants.INDEX_DIR));
-			indexWriterManager = new IndexWriterManager(directory);
-		} catch (IOException e) {
-			e.printStackTrace();
-		}
-	}
+	private static IndexWriterManager indexWriterManager = new IndexWriterManager();
+
+	Logger logger = Logger.getLogger(getClass());
 
 	@Override
-	public Long createIndexs(Boolean fromFiles) {
+	public Long createIndexs(List<Table_name> tableNames, Boolean fromFiles) {
 		if (creatingIndex) {
 			throw new SearchException("已存在线程在创建索引,不可重复请求");
 		}
 		creatingIndex = true;
-		// 如果索引实时更新处于开启状态,需要暂时关闭(以免两者同时操作索引出现问题)
+		// 如果索引实时更新处于开启状态,需要关闭(以免两者同时操作索引出现问题)
 		if (aqListener.isRunning()) {
 			logger.info("索引实时更新服务正在运行,尝试关闭索引实时更新服务...");
 			aqListener.stop();
 		}
 
-		try {
-			indexWriter = indexWriterManager.get();
-			logger.info("正在清理旧索引...");
-			indexWriter.deleteAll();
-			indexWriter.commit();
-			logger.info("旧索引清理完毕");
-			Long startTime = new Date().getTime();
-
-			// 创建各个实体类对应表的索引
-			List<Class<?>> clazzes = ClassAndTableNameUtils.getEntityClasses();
-			for (Class<?> clazz : clazzes) {
-				// 只有明确指定不从本地文件读取数据时,才从数据库获取数据建立索引
-				if (fromFiles != null && !fromFiles) {
-					createIndexes(clazz);
-				} else {
-					createIndexesFromFiles(clazz);
-				}
+		if (CollectionUtils.isEmpty(tableNames)) {
+			tableNames = ClassAndTableNameUtils.getTableNames();
+		}
+		Long startTime = new Date().getTime();
+		// 创建各个表的索引
+		for (Table_name tableName : tableNames) {
+			// 只有明确指定不从本地文件读取数据时,才从数据库获取数据建立索引
+			if (fromFiles != null && !fromFiles) {
+				createIndexes(ClassAndTableNameUtils.toClass(tableName));
+			} else {
+				createIndexesFromFiles(ClassAndTableNameUtils.toClass(tableName));
 			}
-
-			Long endTime = new Date().getTime();
-			logger.info("索引创建成功, 共用时间 " + (endTime - startTime) + " ms");
-			return endTime - startTime;
-		} catch (IOException e) {
-			e.printStackTrace();
-		} catch (InterruptedException e) {
-			e.printStackTrace();
-		} finally {
-			creatingIndex = false;
-			indexWriterManager.release();
 		}
-		return null;
+		creatingIndex = false;
+		Long endTime = new Date().getTime();
+		logger.info(String.format("索引创建成功, 共用时间%.2fs\n", (endTime - startTime) / 1000.0));
+		return endTime - startTime;
 	}
 
 	/**
@@ -128,57 +101,66 @@ public class IndexServiceImpl implements IndexService {
 	 * @return 实际创建的索引的数目
 	 * @throws IOException
 	 */
-	private <T> Long createIndexes(Class<T> clazz) throws IOException {
+	private <T> Long createIndexes(Class<T> clazz) {
 		Long startTime = new Date().getTime();
 		Table_name tableName = ClassAndTableNameUtils.toTableName(clazz);
 		logger.info("正在创建" + tableName.value() + "索引...");
 		JpaRepository<T, Long> dao = ClassAndTableNameUtils.getDao(clazz);
 
-		// 分页获取数据以创建索引
-		PageParams pageParams = new PageParams();
-		pageParams.setPage(1);
-		pageParams.setCount(PAGE_SIZE);
-		PageInfo pageInfo = new PageInfo(pageParams);
-		Page<T> pageResult = dao.findAll(pageInfo);
-
-		// 数据库中数据的总数目
-		long totalElements = pageResult.getTotalElements();
-		logger.info("发现数据:" + totalElements + "条");
-		// 已翻页的数据数目
-		Long size = 0L;
 		// 实际创建的索引数目(可能会有部分数据有问题,不会创建索引)
 		long count = 0;
-		// 用于记录上次提交索引时的创建进度
-		double recordProgress = 0;
-
-		while (totalElements > size) {
-			List<T> content = pageResult.getContent();
-			for (T element : content) {
-				Document document = ObjectToDocumentUtils.toDocument(element);
-				if (document != null) {
-					indexWriter.addDocument(document);
-					count++;
+		// 数据库中数据的总数目
+		long totalElements = 0L;
+		try {
+			deleteIndexs(tableName);
+			// 分页获取数据以创建索引
+			PageParams pageParams = new PageParams();
+			pageParams.setPage(1);
+			pageParams.setCount(PAGE_SIZE);
+			PageInfo pageInfo = new PageInfo(pageParams);
+			Page<T> pageResult = dao.findAll(pageInfo);
+
+			totalElements = pageResult.getTotalElements();
+			logger.info("发现数据:" + totalElements + "条");
+			// 已翻页的数据数目
+			Long size = 0L;
+			// 用于记录上次提交索引时的创建进度
+			double recordProgress = 0;
+
+			while (totalElements > size) {
+				List<T> content = pageResult.getContent();
+				for (T element : content) {
+					Document document = ObjectToDocumentUtils.toDocument(element);
+					if (document != null) {
+						indexWriter.addDocument(document);
+						count++;
+					}
 				}
-			}
-			size += content.size();
+				size += content.size();
 
-			// 器件索引的创建进度(百分比)
-			double indexProgress = size * 100.0 / totalElements;
-			logger.info(String.format("Created...................%.2f%%", indexProgress));
-			// 每创建5%,提交一次,避免内存耗尽,发生OutOfMemoryError
-			if (indexProgress - recordProgress > 5) {
-				indexWriter.commit();
-				recordProgress = indexProgress;
-			}
+				// 器件索引的创建进度(百分比)
+				double indexProgress = size * 100.0 / totalElements;
+				logger.info(String.format("Created...................%.2f%%", indexProgress));
+				// 每创建5%,提交一次,避免内存耗尽,发生OutOfMemoryError
+				if (indexProgress - recordProgress > 5) {
+					indexWriter.commit();
+					recordProgress = indexProgress;
+				}
 
-			pageParams.setPage(pageParams.getPage() + 1);
-			pageInfo = new PageInfo(pageParams);
-			pageResult = dao.findAll(pageInfo);
+				pageParams.setPage(pageParams.getPage() + 1);
+				pageInfo = new PageInfo(pageParams);
+				pageResult = dao.findAll(pageInfo);
+			}
+			indexWriter.commit();
+		} catch (InterruptedException | IOException e) {
+			e.printStackTrace();
+		} finally {
+			indexWriterManager.release(tableName);
 		}
-		indexWriter.commit();
 
 		Long endTime = new Date().getTime();
-		logger.info("创建" + tableName.value() + "索引: " + count + "条,耗时 " + (endTime - startTime) + " ms\n");
+		logger.info(String.format("创建%s索引:%s条,问题数据:%s条,耗时%.2fs\n", tableName.value(), count, (totalElements - count),
+				(endTime - startTime) / 1000.0));
 		return count;
 	}
 
@@ -189,7 +171,7 @@ public class IndexServiceImpl implements IndexService {
 	 * @return 实际创建的索引的数目
 	 * @throws IOException
 	 */
-	private <T> Long createIndexesFromFiles(Class<T> clazz) throws IOException {
+	private <T> Long createIndexesFromFiles(Class<T> clazz) {
 		Table_name tableName = ClassAndTableNameUtils.toTableName(clazz);
 		logger.info("正在创建" + tableName.value() + "索引...");
 		// 数据的总数目
@@ -200,9 +182,10 @@ public class IndexServiceImpl implements IndexService {
 			// 从本地路径读取器件数据
 			File file = new File(getDataPath(tableName));
 			if (!file.exists() || ArrayUtils.isEmpty(file.listFiles())) {
-				logger.error("创建索引失败,原因:" + tableName.value() + "数据文件不存在!");
+				logger.error("创建索引失败,原因:" + tableName.value() + "数据文件不存在!\n");
 				return 0L;
 			}
+			deleteIndexs(tableName);
 			File[] files = file.listFiles();
 			// 将要创建的索引总数目约为:文件数目*单个文件的行数
 			long totalSize = files.length * SINGLE_FILE_MAX_SIZE;
@@ -227,13 +210,32 @@ public class IndexServiceImpl implements IndexService {
 				indexWriter.commit();
 				bufferedReader.close();
 			}
-		} catch (IOException e) {
+		} catch (IOException | InterruptedException e) {
 			e.printStackTrace();
+		} finally {
+			indexWriterManager.release(tableName);
 		}
-		logger.info(tableName.value() + "数据总数:" + totalElements + "条,创建索引:" + count + "条");
+		logger.info(tableName.value() + "数据总数:" + totalElements + "条,创建索引:" + count + "条,问题数据:"
+				+ (totalElements - count) + "条\n");
 		return count;
 	}
 
+	/**
+	 * 删除指定表的索引
+	 * 
+	 * @param tableName
+	 *            表名
+	 * @throws InterruptedException
+	 * @throws IOException
+	 */
+	private void deleteIndexs(Table_name tableName) throws InterruptedException, IOException {
+		indexWriter = indexWriterManager.get(tableName);
+		logger.info("正在清理旧索引..." + tableName.value());
+		indexWriter.deleteAll();
+		indexWriter.commit();
+		logger.info("旧索引清理完毕..." + tableName.value());
+	}
+
 	@Override
 	public Long downloadDataFromDatabase(List<Table_name> tableNames) {
 		Long startTime = new Date().getTime();
@@ -334,14 +336,14 @@ public class IndexServiceImpl implements IndexService {
 			Document document = ObjectToDocumentUtils.toDocument(obj);
 			if (document != null) {
 				try {
-					indexWriter = indexWriterManager.get();
+					indexWriter = indexWriterManager.get(ClassAndTableNameUtils.toTableName(obj.getClass()));
 					indexWriter.addDocument(document);
 					indexWriter.commit();
 					logger.info("Saved... " + obj + "\n");
 				} catch (IOException | InterruptedException e) {
 					e.printStackTrace();
 				} finally {
-					indexWriterManager.release();
+					indexWriterManager.release(ClassAndTableNameUtils.toTableName(obj.getClass()));
 				}
 				return obj;
 			} else {
@@ -357,7 +359,7 @@ public class IndexServiceImpl implements IndexService {
 			Document document = ObjectToDocumentUtils.toDocument(obj);
 			if (document != null) {
 				try {
-					indexWriter = indexWriterManager.get();
+					indexWriter = indexWriterManager.get(ClassAndTableNameUtils.toTableName(obj.getClass()));
 					indexWriter.updateDocument(new Term(ClassAndTableNameUtils.getIdField(obj.getClass()),
 							String.valueOf(ClassAndTableNameUtils.getId(obj))), document);
 					indexWriter.commit();
@@ -365,7 +367,7 @@ public class IndexServiceImpl implements IndexService {
 				} catch (IOException | InterruptedException e) {
 					e.printStackTrace();
 				} finally {
-					indexWriterManager.release();
+					indexWriterManager.release(ClassAndTableNameUtils.toTableName(obj.getClass()));
 				}
 				return obj;
 			} else {
@@ -381,7 +383,7 @@ public class IndexServiceImpl implements IndexService {
 			return null;
 		}
 		try {
-			indexWriter = indexWriterManager.get();
+			indexWriter = indexWriterManager.get(ClassAndTableNameUtils.toTableName(obj.getClass()));
 			indexWriter.deleteDocuments(new Term(ClassAndTableNameUtils.getIdField(obj.getClass()),
 					String.valueOf(ClassAndTableNameUtils.getId(obj))));
 			indexWriter.commit();
@@ -390,7 +392,7 @@ public class IndexServiceImpl implements IndexService {
 		} catch (InterruptedException e) {
 			e.printStackTrace();
 		} finally {
-			indexWriterManager.release();
+			indexWriterManager.release(ClassAndTableNameUtils.toTableName(obj.getClass()));
 		}
 		logger.info("Deleted... " + obj + "\n");
 		return obj;

+ 10 - 3
search-console-b2b/src/main/java/com/uas/search/console/b2b/service/impl/SearchServiceImpl.java

@@ -56,7 +56,7 @@ public class SearchServiceImpl implements SearchService, InnerSearchService {
 	@SuppressWarnings("unchecked")
 	@Override
 	public SPage<Long> searchIds(String keyword, Table_name tableName, PageParams pageParams) throws SearchException {
-		IndexSearcher indexSearcher = SearchUtils.getIndexSearcher();
+		IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(tableName);
 		// 获取该表keyword可以搜索的域
 		List<String> keywordFields = ClassAndTableNameUtils.getKeywordFields(tableName);
 
@@ -231,9 +231,16 @@ public class SearchServiceImpl implements SearchService, InnerSearchService {
 
 	@Override
 	public <T> T getObjectById(Long id, Class<T> clazz) {
+		if (id == null) {
+			return null;
+		}
+		logger.info("id=" + id);
 		try {
-			Document document = SearchUtils.getDocumentById(ClassAndTableNameUtils.getIdField(clazz), id);
-			return DocumentToObjectUtils.toObject(document, clazz);
+			Document document = SearchUtils.getDocumentById(ClassAndTableNameUtils.toTableName(clazz),
+					ClassAndTableNameUtils.getIdField(clazz), id);
+			T element = DocumentToObjectUtils.toObject(document, clazz);
+			logger.info(element);
+			return element;
 		} catch (SearchException e) {
 			e.printStackTrace();
 			return null;

+ 86 - 59
search-console-b2b/src/main/java/com/uas/search/console/b2b/support/IndexSearcherManager.java

@@ -3,6 +3,9 @@ package com.uas.search.console.b2b.support;
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Paths;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.log4j.Logger;
 import org.apache.lucene.index.DirectoryReader;
@@ -11,7 +14,9 @@ import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.store.FSDirectory;
 import org.apache.lucene.store.NIOFSDirectory;
 
-import com.uas.search.console.b2b.util.SearchConstants;
+import com.uas.search.b2b.service.SearchService.Table_name;
+import com.uas.search.console.b2b.util.ClassAndTableNameUtils;
+import com.uas.search.console.b2b.util.SearchUtils;
 
 /**
  * 将索引加入缓存,对IndexSearcher进行管理
@@ -21,112 +26,132 @@ import com.uas.search.console.b2b.util.SearchConstants;
  */
 public class IndexSearcherManager {
 
-	private IndexSearcher currentSearcher;
+	/**
+	 * 用于存放指定表的索引IndexSearcher
+	 */
+	private Map<Table_name, IndexSearcher> indexSearchers = new ConcurrentHashMap<>();
 
-	private FSDirectory directory;
+	/**
+	 * 标识指定表的IndexSearcher是否正在重新打开
+	 */
+	private Map<Table_name, Boolean> reopening = new ConcurrentHashMap<>();
 
-	private Logger logger = Logger.getLogger(IndexSearcherManager.class);
+	private Logger logger = Logger.getLogger(getClass());
 
 	public IndexSearcherManager() {
-		try {
-			String os = System.getProperty("os.name");
-			// 本来NIOFSDirectory速度更快,但因为jre的bug,在Windows平台上,其性能很差
-			if (os.toLowerCase().startsWith("win")) {
-				directory = FSDirectory.open(Paths.get(SearchConstants.INDEX_DIR));
-			} else {
-				directory = NIOFSDirectory.open(Paths.get(SearchConstants.INDEX_DIR));
-			}
-			warm();
-		} catch (IOException e) {
-			e.printStackTrace();
+		List<Table_name> tableNames = ClassAndTableNameUtils.getTableNames();
+		for (Table_name tableName : tableNames) {
+			reopening.put(tableName, false);
 		}
 	}
 
-	public IndexSearcherManager(FSDirectory dir) {
-		directory = dir;
-		warm();
-	}
-
 	/**
-	 * 对currentSearcher进行初始化操作
+	 * 对指定表的IndexSearcher进行初始化操作
 	 * 
-	 * @throws IOException
+	 * @param tableName
+	 *            表名
+	 * @return 指定表的FSDirectory
 	 */
-	public synchronized void warm() {
-		File[] files = directory.getDirectory().toFile().listFiles();
-		// 不为空的话说明索引已成功加载
-		if (currentSearcher == null) {
-			// 路径不为空文件夹
-			if (files.length != 0) {
-				try {
-					currentSearcher = new IndexSearcher(DirectoryReader.open(directory));
-				} catch (IOException e) {
-					e.printStackTrace();
-				}
+	public synchronized FSDirectory warm(Table_name tableName) {
+		String indexPath = SearchUtils.getIndexPath(tableName);
+		FSDirectory directory = null;
+		IndexSearcher indexSearcher = indexSearchers.get(tableName);
+		// 本来NIOFSDirectory速度更快,但因为jre的bug,在Windows平台上,其性能很差
+		try {
+			if (System.getProperty("os.name").toLowerCase().startsWith("win")) {
+				directory = FSDirectory.open(Paths.get(indexPath));
 			} else {
-				logger.error("索引文件不存在!");
+				directory = NIOFSDirectory.open(Paths.get(indexPath));
+			}
+
+			File[] files = directory.getDirectory().toFile().listFiles();
+			// 不为空的话说明索引已成功加载
+			if (indexSearcher == null) {
+				// 路径不为空文件夹
+				if (files.length != 0) {
+					try {
+						indexSearcher = new IndexSearcher(DirectoryReader.open(directory));
+						indexSearchers.put(tableName, indexSearcher);
+					} catch (IOException e) {
+						e.printStackTrace();
+					}
+				} else {
+					logger.error("索引文件不存在!");
+				}
 			}
+		} catch (IOException e) {
+			logger.error("索引路径打开失败:" + indexPath);
+			e.printStackTrace();
 		}
+		return directory;
 	}
 
 	/**
-	 * 内存中IndexReader(可能)更新,根据版本号确定是否更新
+	 * 内存中指定表的IndexReader(可能需要)更新,根据版本号确定是否更新
 	 * 
-	 * @throws InterruptedException
-	 * @throws IOException
+	 * @param tableName
+	 *            表名
 	 */
-	public void maybeReopen() {
-		startReopen();
+	public void maybeReopen(Table_name tableName) {
+		if (tableName == null) {
+			return;
+		}
+		startReopen(tableName);
 
 		try {
 			// 每次都要进行初始化处理,是为了防止加载索引时,索引为空,而后来索引不为空时,却不能够正确加载
-			warm();
+			FSDirectory directory = warm(tableName);
+			IndexSearcher currentSearcher = indexSearchers.get(tableName);
 			if (currentSearcher == null) {
 				return;
 			}
-			IndexSearcher searcher = get();
+			IndexSearcher searcher = get(tableName);
 			Long currentVersion = ((DirectoryReader) searcher.getIndexReader()).getVersion();
 			// 版本号不一致(本地索引有更改),更新IndexReader
 			try {
 				if (DirectoryReader.open(directory).getVersion() != currentVersion) {
 					IndexReader newReader = DirectoryReader.open(directory);
 					IndexSearcher newSearcher = new IndexSearcher(newReader);
-					swapIndexSearcher(newSearcher);
+					updateIndexSearcher(tableName, newSearcher);
 				}
 			} catch (IOException e) {
 				e.printStackTrace();
 			}
 			release(searcher);
 		} finally {
-			doneReopen();
+			doneReopen(tableName);
 		}
 
 	}
 
-	private boolean reopening = false;
-
-	private synchronized void startReopen() {
-		while (reopening) {
+	private synchronized void startReopen(Table_name tableName) {
+		while (reopening.get(tableName)) {
 			try {
 				wait();
 			} catch (InterruptedException e) {
 				e.printStackTrace();
 			}
 		}
-		reopening = true;
+		reopening.put(tableName, true);
 	}
 
-	private synchronized void doneReopen() {
-		reopening = false;
+	private synchronized void doneReopen(Table_name tableName) {
+		reopening.put(tableName, false);
 		notifyAll();
 	}
 
 	/**
-	 * 得到IndexSearcher
+	 * 得到指定表的IndexSearcher
 	 * 
+	 * @param tableName
+	 *            表名
 	 * @return
 	 */
-	public synchronized IndexSearcher get() {
+	public synchronized IndexSearcher get(Table_name tableName) {
+		if (tableName == null) {
+			return null;
+		}
+		IndexSearcher currentSearcher = indexSearchers.get(tableName);
 		if (currentSearcher != null) {
 			currentSearcher.getIndexReader().incRef();
 		}
@@ -134,14 +159,14 @@ public class IndexSearcherManager {
 	}
 
 	/**
-	 * 释放indexSearcher,将对indexReader的引用减1,为0时成为垃圾
+	 * 释放indexSearcher
 	 * 
 	 * @param indexSearcher
-	 * @throws IOException
 	 */
 	public synchronized void release(IndexSearcher indexSearcher) {
 		if (indexSearcher != null) {
 			try {
+				// 对indexReader的引用减1,为0时成为垃圾
 				indexSearcher.getIndexReader().decRef();
 			} catch (IOException e) {
 				e.printStackTrace();
@@ -150,14 +175,16 @@ public class IndexSearcherManager {
 	}
 
 	/**
-	 * 更新IndexSearcher
+	 * 更新指定表的IndexSearcher
 	 * 
-	 * @param indexSearcher
+	 * @param tableName
+	 *            表名
+	 * @param newIndexSearcher
 	 *            新IndexSearcher
-	 * @throws IOException
 	 */
-	private synchronized void swapIndexSearcher(IndexSearcher indexSearcher) {
+	private synchronized void updateIndexSearcher(Table_name tableName, IndexSearcher newIndexSearcher) {
+		IndexSearcher currentSearcher = indexSearchers.get(tableName);
 		release(currentSearcher);
-		currentSearcher = indexSearcher;
+		indexSearchers.put(tableName, newIndexSearcher);
 	}
 }

+ 69 - 21
search-console-b2b/src/main/java/com/uas/search/console/b2b/support/IndexWriterManager.java

@@ -1,12 +1,22 @@
 package com.uas.search.console.b2b.support;
 
 import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
+import org.apache.log4j.Logger;
 import org.apache.lucene.index.IndexWriter;
 import org.apache.lucene.index.IndexWriterConfig;
 import org.apache.lucene.store.FSDirectory;
+import org.apache.lucene.store.NIOFSDirectory;
 import org.wltea.analyzer.lucene.IKAnalyzer;
 
+import com.uas.search.b2b.service.SearchService.Table_name;
+import com.uas.search.console.b2b.util.ClassAndTableNameUtils;
+import com.uas.search.console.b2b.util.SearchUtils;
+
 /**
  * 对IndexWriter进行管理,防止同时有多个方法对索引进行修改,抛出LockObtainFailedException异常
  * 
@@ -15,52 +25,90 @@ import org.wltea.analyzer.lucene.IKAnalyzer;
  */
 public class IndexWriterManager {
 
-	private IndexWriter indexWriter;
+	/**
+	 * 用于存放指定表的索引IndexWriter
+	 */
+	private Map<Table_name, IndexWriter> indexWriters = new ConcurrentHashMap<>();
+
+	/**
+	 * 标识指定表的索引是否正在使用
+	 */
+	private Map<Table_name, Boolean> using = new ConcurrentHashMap<>();
 
-	private FSDirectory directory;
+	private Logger logger = Logger.getLogger(getClass());
 
-	public IndexWriterManager(FSDirectory dir) {
-		directory = dir;
+	public IndexWriterManager() {
+		List<Table_name> tableNames = ClassAndTableNameUtils.getTableNames();
+		for (Table_name tableName : tableNames) {
+			using.put(tableName, false);
+		}
 	}
 
 	/**
-	 * 得到IndexWriter,用完后需调用release释放IndexWriter
+	 * 得到指定表的IndexWriter,用完后需调用release释放IndexWriter
 	 * 
-	 * @return
-	 * @throws IOException
+	 * @param tableName
+	 *            表名
+	 * @return IndexWriter
 	 * @throws InterruptedException
 	 */
-	public synchronized IndexWriter get() throws IOException, InterruptedException {
-		startUsing();
+	public synchronized IndexWriter get(Table_name tableName) throws InterruptedException {
+		if (tableName == null) {
+			return null;
+		}
+		startUsing(tableName);
+		IndexWriter indexWriter = indexWriters.get(tableName);
 		if (indexWriter == null) {
 			// 此处用IK
 			IndexWriterConfig config = new IndexWriterConfig(new IKAnalyzer());
-			indexWriter = new IndexWriter(directory, config);
+			// 获取表对应的索引目录
+			String indexPath = SearchUtils.getIndexPath(tableName);
+			try {
+				FSDirectory directory = null;
+				// 本来NIOFSDirectory速度更快,但因为jre的bug,在Windows平台上,其性能很差
+				if (System.getProperty("os.name").toLowerCase().startsWith("win")) {
+					directory = FSDirectory.open(Paths.get(indexPath));
+				} else {
+					directory = NIOFSDirectory.open(Paths.get(indexPath));
+				}
+				indexWriter = new IndexWriter(directory, config);
+				indexWriters.put(tableName, indexWriter);
+				return indexWriter;
+			} catch (IOException e) {
+				logger.error("索引路径打开失败:" + indexPath);
+				e.printStackTrace();
+				return null;
+			}
+		} else {
+			return indexWriter;
 		}
-		return indexWriter;
 	}
 
 	/**
-	 * 释放对indexWriter的使用
+	 * 释放对指定表的indexWriter的使用
+	 * 
+	 * @param tableName
+	 *            表名
 	 */
-	public synchronized void release() {
+	public synchronized void release(Table_name tableName) {
+		if (tableName == null) {
+			throw new NullPointerException("参数不能为空:tableName");
+		}
 		// indexWriter一直保持打开状态,也只存在一个实例,
 		// indexWriter用完后只是释放对其的占用,不会进行close
 		// TODO close方法有时会卡住(多出现在插入记录之后),暂时还未找到原因
-		doneUsing();
+		doneUsing(tableName);
 	}
 
-	private boolean using = false;
-
-	private synchronized void startUsing() throws InterruptedException {
-		while (using) {
+	private synchronized void startUsing(Table_name tableName) throws InterruptedException {
+		while (using.get(tableName)) {
 			wait();
 		}
-		using = true;
+		using.put(tableName, true);
 	}
 
-	private synchronized void doneUsing() {
-		using = false;
+	private synchronized void doneUsing(Table_name tableName) {
+		using.put(tableName, false);
 		notifyAll();
 	}
 }

+ 46 - 19
search-console-b2b/src/main/java/com/uas/search/console/b2b/util/SearchUtils.java

@@ -24,6 +24,7 @@ import org.springframework.util.StringUtils;
 import org.wltea.analyzer.lucene.IKAnalyzer;
 
 import com.uas.search.b2b.exception.SearchException;
+import com.uas.search.b2b.service.SearchService.Table_name;
 import com.uas.search.console.b2b.support.IndexSearcherManager;
 
 /**
@@ -92,13 +93,15 @@ public class SearchUtils {
 	}
 
 	/**
-	 * 获取IndexSearcher对象,若为空,抛出异常
+	 * 获取指定表的IndexSearcher对象,若为空,抛出异常
 	 * 
+	 * @param tableName
+	 *            表名
 	 * @return IndexSearcher对象
 	 */
-	public static IndexSearcher getIndexSearcher() {
-		searcherManager.maybeReopen();
-		IndexSearcher indexSearcher = searcherManager.get();
+	public static IndexSearcher getIndexSearcher(Table_name tableName) {
+		searcherManager.maybeReopen(tableName);
+		IndexSearcher indexSearcher = searcherManager.get(tableName);
 		if (indexSearcher == null) {
 			throw new SearchException("获取索引文件失败");
 		}
@@ -115,19 +118,21 @@ public class SearchUtils {
 	}
 
 	/**
-	 * 根据域和搜索词获取Document(每个id最多只能对应一条数据)
+	 * 根据域和搜索词获取指定表的Document(每个id最多只能对应一条数据)
 	 * 
+	 * @param tableName
+	 *            表名
 	 * @param field
 	 *            搜索域
 	 * @param keyword
 	 *            搜索词
 	 * @return Document
 	 */
-	public static Document getDocumentById(String field, Long id) throws SearchException {
+	public static Document getDocumentById(Table_name tableName, String field, Long id) throws SearchException {
 		if (id == null) {
 			throw new SearchException("输入无效:" + id);
 		}
-		List<Document> documents = getDocuments(field, Long.toString(id));
+		List<Document> documents = getDocuments(tableName, field, Long.toString(id));
 		if (CollectionUtils.isEmpty(documents)) {
 			return null;
 		} else if (documents.size() > 1) {
@@ -137,21 +142,25 @@ public class SearchUtils {
 	}
 
 	/**
-	 * 根据域和搜索词获取Document列表
+	 * 根据域和搜索词获取指定表的Document列表
 	 * 
+	 * @param tableName
+	 *            表名
 	 * @param field
 	 *            搜索域
 	 * @param keyword
 	 *            搜索词
 	 * @return Document列表
 	 */
-	public static List<Document> getDocuments(String field, String keyword) {
-		return getDocuments(field, keyword, TOP_NUM);
+	public static List<Document> getDocuments(Table_name tableName, String field, String keyword) {
+		return getDocuments(tableName, field, keyword, TOP_NUM);
 	}
 
 	/**
-	 * 根据域和搜索词获取指定数目的Document列表
+	 * 根据域和搜索词获取指定表的指定数目的Document列表
 	 * 
+	 * @param tableName
+	 *            表名
 	 * @param field
 	 *            搜索域
 	 * @param keyword
@@ -160,42 +169,46 @@ public class SearchUtils {
 	 *            指定的数目(数目最多为该数目)
 	 * @return
 	 */
-	public static List<Document> getDocuments(String field, String keyword, int topNum) {
+	public static List<Document> getDocuments(Table_name tableName, String field, String keyword, int topNum) {
 		if (StringUtils.isEmpty(field) || StringUtils.isEmpty(keyword)) {
 			throw new SearchException("搜索的域和搜索词不能为空");
 		}
 		TermQuery termQuery = new TermQuery(new Term(field, keyword));
-		return getDocuments(termQuery, topNum);
+		return getDocuments(tableName, termQuery, topNum);
 	}
 
 	/**
-	 * 根据查询条件获取Document列表
+	 * 根据查询条件获取指定表的Document列表
 	 * 
+	 * @param tableName
+	 *            表名
 	 * @param query
 	 *            查询条件
 	 * @return
 	 */
-	public static List<Document> getDocuments(Query query) {
-		return getDocuments(query, TOP_NUM);
+	public static List<Document> getDocuments(Table_name tableName, Query query) {
+		return getDocuments(tableName, query, TOP_NUM);
 	}
 
 	/**
-	 * 根据查询条件获取指定数目的Document列表
+	 * 根据查询条件获取指定表的指定数目的Document列表
 	 * 
+	 * @param tableName
+	 *            表名
 	 * @param query
 	 *            查询条件
 	 * @param topNum
 	 *            指定的数目(数目最多为该数目)
 	 * @return
 	 */
-	public static List<Document> getDocuments(Query query, int topNum) {
+	public static List<Document> getDocuments(Table_name tableName, Query query, int topNum) {
 		if (query == null) {
 			throw new SearchException("query不能为null");
 		}
 		if (topNum < 1) {
 			throw new SearchException("查询的数目topNum不能小于1");
 		}
-		IndexSearcher indexSearcher = getIndexSearcher();
+		IndexSearcher indexSearcher = getIndexSearcher(tableName);
 		List<Document> documents = new ArrayList<>();
 		try {
 			TopDocs topDocs = indexSearcher.search(query, topNum);
@@ -211,6 +224,20 @@ public class SearchUtils {
 		return documents;
 	}
 
+	/**
+	 * 获取指定表的索引路径
+	 * 
+	 * @param tableName
+	 *            指定表
+	 * @return 索引路径
+	 */
+	public static String getIndexPath(Table_name tableName) {
+		if (tableName == null) {
+			return null;
+		}
+		return SearchConstants.INDEX_DIR + "/" + tableName.value();
+	}
+
 	/**
 	 * 递归删除文件(夹)
 	 * 

+ 5 - 17
search-console-b2b/src/main/webapp/WEB-INF/views/console.html

@@ -10,7 +10,7 @@
 		<ol>
 			<strong><li class="title">搜索</li></strong>
 			<ol>
-				<li>search?keyword=PMP150&tableName=PURC$ORDERS&filters={"pu_status":200,"pu_enuu":10041166,"fromDate":1435680000000,"endDate":1479214994000}&sort=[{"field":"pu_id","reverse":1,"type":"LONG"},{"field":"pu_date","reverse":1,"type":"LONG","missingValue":2433211087000}]</li>
+				<li>search?keyword=PMP150&tableName=PURC$ORDERS&filters={"pu_status":200,"pu_enuu":10041166,"fromDate":1435680000000,"endDate":1479214994000}&sort=[{"field":"pu_id","reverse":1,"type":"LONG"},{"field":"pu_date","reverse":true,"type":"LONG","missingValue":2433211087000}]</li>
 				<li><a target="_blank">search?keyword=PMP1506000&tableName=PURC$ORDERS</a></li>
 				<li><a target="_blank">search?keyword=MSQ150800&tableName=MAKE$ORDERS</a></li>
 			</ol>
@@ -23,9 +23,11 @@
 
 			<strong><li class="title">索引修改</li></strong>
 			<ol>
+				<li>index/create?tableNames=purc$badin,purc$badout&fromFiles=false</li>
 				<li><a target="_blank">index/create</a></li>
-				<li><a target="_blank">index/create?fromFiles=0</a></li>
-				<li><a target="_blank">index/listen/start?waitInterval=10</a></li>
+				<li><a target="_blank">index/create?tableNames=purc$badin,purc$badout</a></li>
+				<li><a target="_blank">index/create?fromFiles=false</a></li>
+				<li><a target="_blank">index/listen/start?waitInterval=3</a></li>
 				<li><a target="_blank">index/listen/stop</a></li>
 				<li><a target="_blank">index/listen/restart</a></li>
 				<li><a target="_blank">index/downloadData</a></li>
@@ -38,20 +40,6 @@
 				<li><a target="_blank">fileUpload</a></li>
 			</ol>
 
-			<strong><li class="title">维护</li></strong>
-			<ol>
-				<li><a target="_blank">index/maintain?tableName=PURC$ORDERS&ids=12271078,12271172,12271173,12271174,12271175,12271176,12271177,12271178,12271179,12271180,12271181,12271182,12271183,12271206,12271207,12271237,12271086,12271087,12271088,12271082,12271165,12271115,12271238,12271239,12271079,12271234,12271089,12271229,12271230,12271231,12271241,12271242,12271243,12271116,12271117,12271118,12271119,12271208,12271163,12271164,12271171,12271232,12271233,12271224,12271225,12271226,12271227,12271228,12271236,12271168,12271085,12271192,12271221,12271222,12271223,12271218,12271083,12271120,12271091,12271162,12271202,12271203,12271214,12271081,12271080,12271184,12271185,12271186,12271187,12271188,12271189,12271190,12271191,12271193,12271160</a></li>
-				<li><a target="_blank">index/maintain?tableName=PURC$ACCEPT&ids=230814,230937,230803,230852,230853,230854,230907,230965,230752,230753,230774,230775,230776,230779,230780,230900,230901,230747,230786,230842,230844,230958,230746,230767,230915,230916,230917,230777,230778,230823,230734,230735,230749,230750,230816,230817,230829,230931,230940,230971,230754,230755,230824,230855,230862,230863,230913,230914,230966,230782,230793,230832,230833,230834,230799,230929,230841,230895,230896,230897,230918,230919,230836,230904,230934,230935,230943,230944,230959,230926,230932,230960,230830,230902,230920,230921,230743,230840,230738,230761,230762,230825,230826,230827,230828,230784,230797,230798,230800,230801,230802,230818,230859,230860,230939,230946,230947,230819,230849,230912,230925,230748,230805,230961,230954,230969,230970,230741,230846,230923,230924,230942,230789,230773,230856,230857,230858,230790,230806,230936,230938,230740,230787,230788,230794,230795,230820,230821,230822,230910,230796,230811,230911,230957,230962,230807,230808,230968,230758,230759,230769,230770,230771,230843,230742,230733,230812,230813,230815,230847,230908,230909,230948,230949,230950,230951,230952,230953,230963,230964,230763,230764,230766,230768,230809,230810,230864,230930,230751,230756,230757,230760,230781,230785,230933,230765,230783,230791,230831,230898,230899,230922,230967,230804,230955,230956,230792,230835,230838,230839,230905,230906,230945,230744,230745,230772,230927,230928</a></li>
-				<li><a target="_blank">index/maintain?tableName=PURC$BADIN&ids=13473,13476,13477,13482,13483,13484,13480,13481,13485,13487,13488,13474,13478,13479,13470,13489,13486,13471,13475,13472</a></li>
-				<li><a target="_blank">index/maintain?tableName=PURC$INQUIRYITEMS&ids=941238,941239,941242,941237,941236,941240,941244,941243,941253,941246,941247,941245,941259,941260,941258,941241,941252,941251,941254,941256,941257,941255,941249,941250,941248</a></li>
-				<li><a target="_blank">index/maintain?tableName=PURC$CHANGES&ids=37526,37527,37528,37522,37535,37536,37537,37520,37533,37521,37534</a></li>
-				<li><a target="_blank">index/maintain?tableName=PURC$PROOFING&ids=4590,4597,4600,4594,4595,4596,4602,4588,4599,4589,4598,4601,4591,4592,4593</a></li>
-				<li><a target="_blank">index/maintain?tableName=PURC$RETURN&ids=57872869,57872874,57872864,57872889,57872157,57872879,57872884,57872978,57872140</a></li>
-				<li><a target="_blank">index/maintain?tableName=PURC$NOTICE&ids=612089,612090,612091,612092,612093,612094,612095,612096,612097,612098,612099,612100,612101,612102,612103,612104,612105,612106,612107</a></li>
-				<li><a target="_blank">index/maintain?tableName=PURC$APCHECK&ids=6394,6391,6400,6399,6392,6395,6401,6402,6397,6398</a></li>
-				<li><a target="_blank">index/maintain?tableName=SALE$SEND&ids=114528,114541,114546,114524,114561,114552,114530,114553,114543,114557,114559,114522,114539,114542,114544,114558,114550,114555,114560,114535,114536,114563,114531,114533,114534,114549,114529,114538,114540,114547,114527,114556,114562,114545,114554,114523,114525,114526,114532,114548,114551,114537</a></li>
-				<li><a target="_blank">index/maintain?tableName=MAKE$ACCEPT&ids=8266,8272,8265,8263,8267,8268,8269,8270,8271,8273,8274,8275,8276,8277,8278</a></li>
-			</ol>
 		</ol>
 	</div>
 </body>