@Override
 public AddResponse add(Collection<InputDocument> inputDocuments) {
   try {
     if (logger.isDebugEnabled()) {
       logger.debug("adding documents...");
     }
     for (InputDocument inputDocument : inputDocuments) {
       assertIdExist(inputDocument);
     }
     for (Document document : DocumentTransformUtil.toLuceneDocuments(inputDocuments, schema)) {
       indexWriter.updateDocument(
           new Term(schema.getIdName(), document.getFieldable(schema.getIdName()).stringValue()),
           document,
           schema.getAnalyzer());
     }
     updateCount.addAndGet(inputDocuments.size());
     if (logger.isDebugEnabled()) {
       logger.debug("add documents finish.");
     }
   } catch (Exception e) {
     logger.error("add documents error", e);
     return new AddResponse(e.getMessage(), ResultCodes.COMMON_ERROR);
   }
   return new AddResponse();
 }
  @Override
  public DeleteResponse deleteByIds(List<String> ids) {
    if (CollectionUtil.isEmpty(ids)) {
      return new DeleteResponse();
    }

    final String idName = schema.getIdName();
    Term[] terms = new Term[ids.size()];
    int index = 0;
    for (String id : ids) {
      terms[index++] = new Term(idName, id);
    }
    try {
      if (logger.isDebugEnabled()) {
        logger.debug("deleting documents...");
      }
      indexWriter.deleteDocuments(terms);
      updateCount.addAndGet(ids.size());
      if (logger.isDebugEnabled()) {
        logger.debug("delete documents finish.");
      }
    } catch (IOException e) {
      logger.error("delete error", e);
      return new DeleteResponse(e.getMessage(), ResultCodes.COMMON_ERROR);
    }
    return new DeleteResponse();
  }
 public OperationResponse commitAndOptimize() {
   try {
     if (logger.isDebugEnabled()) {
       logger.debug("commiting...");
     }
     indexWriter.commit();
     if (logger.isDebugEnabled()) {
       logger.debug("commit finish.");
     }
     if (logger.isDebugEnabled()) {
       logger.debug("optimizing...");
     }
     indexWriter.forceMerge(defaultMergeSize);
     if (logger.isDebugEnabled()) {
       logger.debug("optimize finish.");
     }
     reopenSearcher();
   } catch (IOException e) {
     logger.error("optimize error", e);
     return new OperationResponse(e.getMessage(), ResultCodes.COMMON_ERROR);
   } catch (OutOfMemoryError e) {
     CloseUtil.close(indexWriter);
     logger.error("error of OOM", e);
     return new OperationResponse(e.getMessage(), ResultCodes.COMMON_ERROR);
   }
   return new OperationResponse();
 }
 @Override
 public void close() {
   CloseUtil.close(indexSearcher);
   CloseUtil.close(indexReader);
   CloseUtil.close(indexWriter);
   CloseUtil.close(directory);
   opened.set(false);
   if (logger.isWarnEnabled()) {
     logger.warn("search engine has been closed.");
   }
 }
  @Override
  public QueryResponse query(SearchQuery searchQuery) {
    try {
      if (logger.isDebugEnabled()) {
        logger.debug("searching query...");
      }
      long start = System.currentTimeMillis();
      QueryParser queryParser =
          new QueryParser(
              LuceneConfig.LUCENE_VERSION, schema.getDefaultSearchField(), schema.getAnalyzer());
      Query query = queryParser.parse(searchQuery.getQuery());
      int pageNo = searchQuery.getPageNo();
      int pageSize = searchQuery.getPageSize();
      int fullPageCount = pageNo * pageSize;
      int pageStartIndex = pageNo < 1 ? 0 : ((pageNo - 1) * pageSize);
      Sort sort = getSort(searchQuery);
      Filter filter = getFilter(searchQuery);
      TopFieldDocs topFieldDocs = indexSearcher.search(query, filter, fullPageCount, sort);
      ScoreDoc[] scoreDocs = topFieldDocs.scoreDocs;
      int scoreDocsLength = scoreDocs.length;
      List<OutputDocument> outputDocuments;

      if (scoreDocsLength <= pageStartIndex) {
        // 当前页没有数据了
        outputDocuments = CollectionUtil.newArrayList(0);
      } else {
        // 只获取最后一页的数据
        outputDocuments = CollectionUtil.newArrayList(scoreDocs.length - pageStartIndex);
        for (int i = pageStartIndex; i < scoreDocs.length; ++i) {
          Document doc = indexSearcher.doc(scoreDocs[i].doc);
          OutputDocument outputDocument = DocumentTransformUtil.toOutputDocument(doc, schema);
          outputDocuments.add(outputDocument);
        }
      }
      QueryResponse queryResponse = new QueryResponse();

      queryResponse.setOutputDocuments(outputDocuments);
      queryResponse.setTotalHits(topFieldDocs.totalHits);

      if (logger.isDebugEnabled()) {
        logger.debug("search query finish.");
      }
      long end = System.currentTimeMillis();
      long timeEscape = end - start;
      queryResponse.setTimeEscape(timeEscape);
      return queryResponse;
    } catch (Exception e) {
      logger.error("search query error", e);
      return new QueryResponse(e.getMessage(), ResultCodes.COMMON_ERROR);
    }
  }
 @Override
 public OperationResponse rollback() {
   try {
     if (logger.isDebugEnabled()) {
       logger.debug("rollbacking...");
     }
     indexWriter.rollback();
     if (logger.isDebugEnabled()) {
       logger.debug("rollback finish.");
     }
   } catch (IOException e) {
     logger.error("rollback error", e);
     return new OperationResponse(e.getMessage(), ResultCodes.COMMON_ERROR);
   }
   return new OperationResponse();
 }
 @Override
 public OperationResponse reopen() {
   try {
     reopenSearcher();
   } catch (IOException e) {
     logger.error("repen error", e);
     return new OperationResponse(e.getMessage(), ResultCodes.COMMON_ERROR);
   }
   return new OperationResponse();
 }
 @Override
 public AddResponse add(InputDocument inputDocument) {
   try {
     if (logger.isDebugEnabled()) {
       logger.debug("adding document...");
     }
     assertIdExist(inputDocument);
     Document document = DocumentTransformUtil.toLuceneDocument(inputDocument, schema);
     indexWriter.updateDocument(
         new Term(schema.getIdName(), document.getFieldable(schema.getIdName()).stringValue()),
         document,
         schema.getAnalyzer());
     updateCount.incrementAndGet();
     if (logger.isDebugEnabled()) {
       logger.debug("add document finish.");
     }
   } catch (IOException e) {
     return new AddResponse(e.getMessage(), ResultCodes.COMMON_ERROR);
   }
   return new AddResponse();
 }
  protected void open(Directory directory) throws IOException {
    IndexWriter newIndexWriter = null;
    IndexReader newIndexReader = null;
    IndexSearcher newIndexSearcher = null;
    if (logger.isDebugEnabled()) {
      logger.debug("opening directory...");
    }
    try {
      IndexWriterConfig indexWriterConfig =
          new IndexWriterConfig(LuceneConfig.LUCENE_VERSION, schema.getAnalyzer());
      newIndexWriter = new IndexWriter(directory, indexWriterConfig);
      newIndexReader = IndexReader.open(newIndexWriter, true);
      newIndexSearcher = new IndexSearcher(newIndexReader);

      synchronized (this) {
        IndexWriter oldIndexWriter = this.indexWriter;
        IndexSearcher oldIndexSearcher = this.indexSearcher;
        IndexReader oldIndexReader = this.indexReader;

        this.indexWriter = newIndexWriter;
        this.indexSearcher = newIndexSearcher;
        this.indexReader = newIndexReader;

        CloseUtil.close(oldIndexSearcher);
        CloseUtil.close(oldIndexReader);
        CloseUtil.close(oldIndexWriter);
      }
      if (logger.isDebugEnabled()) {
        logger.debug("open directory finish.");
      }
    } catch (IOException e) {
      logger.error("open directory error", e);
      CloseUtil.close(newIndexSearcher);
      CloseUtil.close(newIndexReader);
      CloseUtil.close(newIndexWriter);
      throw e;
    }
  }
 protected void reopenSearcher() throws IOException {
   try {
     reopenLock.lock();
     if (updateCount.get() > 0) {
       logger.info("proccessing reopen...");
       IndexReader oldIndexReader = this.indexReader;
       IndexSearcher oldIndexSearcher = this.indexSearcher;
       IndexReader newIndexReader = IndexReader.openIfChanged(oldIndexReader);
       if (newIndexReader != null) {
         IndexSearcher newIndexSearcher = new IndexSearcher(newIndexReader);
         indexSearcher = newIndexSearcher;
         indexReader = newIndexReader;
         CloseUtil.close(oldIndexReader);
         CloseUtil.close(oldIndexSearcher);
       }
       updateCount.set(0);
       logger.info("proccess reopen finish.");
     } else {
       logger.info("no thing to reopen");
     }
   } finally {
     reopenLock.unlock();
   }
 }
 @Override
 public OperationResponse commit() {
   try {
     if (logger.isDebugEnabled()) {
       logger.debug("commiting...");
     }
     indexWriter.commit();
     if (logger.isDebugEnabled()) {
       logger.debug("commit finish.");
     }
     reopenSearcher();
   } catch (IOException e) {
     logger.error("commit error", e);
     return new OperationResponse(e.getMessage(), ResultCodes.COMMON_ERROR);
   } catch (OutOfMemoryError e) {
     CloseUtil.close(indexWriter);
     logger.error("error of OOM", e);
     // TODO reopen writer?
     return new OperationResponse(e.getMessage(), ResultCodes.COMMON_ERROR);
   }
   return new OperationResponse();
 }