/* * Copyright 2015-Present Entando Inc. (http://www.entando.com) All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.agiletec.plugins.jacms.aps.system.services.searchengine; import com.agiletec.aps.system.common.entity.model.AttributeSearchInfo; import com.agiletec.aps.system.common.entity.model.AttributeTracer; import com.agiletec.aps.system.common.entity.model.IApsEntity; import com.agiletec.aps.system.common.entity.model.attribute.AttributeInterface; import com.agiletec.aps.system.common.searchengine.IndexableAttributeInterface; import com.agiletec.aps.system.common.util.EntityAttributeIterator; import com.agiletec.aps.system.exception.ApsSystemException; import com.agiletec.aps.system.services.category.Category; import com.agiletec.aps.system.services.lang.ILangManager; import com.agiletec.aps.system.services.lang.Lang; import com.agiletec.plugins.jacms.aps.system.services.content.model.extraAttribute.ResourceAttributeInterface; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.document.DateTools; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.document.IntField; import org.apache.lucene.document.StringField; import org.apache.lucene.document.TextField; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.index.Term; import org.apache.lucene.store.Directory; import org.apache.lucene.store.FSDirectory; import org.apache.lucene.store.SimpleFSLockFactory; import org.apache.lucene.util.Version; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Data Access Object dedita alla indicizzazione di documenti. * @author E.Santoboni */ public class IndexerDAO implements IIndexerDAO { private static final Logger _logger = LoggerFactory.getLogger(IndexerDAO.class); /** * Inizializzazione dell'indicizzatore. * @param dir La cartella locale contenitore dei dati persistenti. * @throws ApsSystemException In caso di errore */ @Override public void init(File dir) throws ApsSystemException { try { this._dir = FSDirectory.open(dir); this._dir.setLockFactory(new SimpleFSLockFactory(dir)); } catch (Throwable t) { _logger.error("Error creating directory", t); throw new ApsSystemException("Error creating directory", t); } _logger.debug("Indexer: search engine index ok."); } @Override public synchronized void add(IApsEntity entity) throws ApsSystemException { IndexWriter writer = null; try { writer = new IndexWriter(this._dir, this.getIndexWriterConfig()); Document document = this.createDocument(entity); writer.addDocument(document); } catch (Throwable t) { _logger.error("Errore saving entity {}", entity.getId(), t); throw new ApsSystemException("Error saving entity", t); } finally { if (null != writer) { try { writer.close(); } catch (IOException ex) { _logger.error("Error closing IndexWriter", ex); } } } } /** * Crea un oggetto Document pronto per l'indicizzazione da un oggetto Content. * @param entity Il contenuto dal quale ricavare il Document. * @return L'oggetto Document ricavato dal contenuto. * @throws ApsSystemException In caso di errore */ protected Document createDocument(IApsEntity entity) throws ApsSystemException { Document document = new Document(); document.add(new StringField(CONTENT_ID_FIELD_NAME, entity.getId(), Field.Store.YES)); document.add(new TextField(CONTENT_TYPE_FIELD_NAME, entity.getTypeCode(), Field.Store.YES)); document.add(new StringField(CONTENT_GROUP_FIELD_NAME, entity.getMainGroup(), Field.Store.YES)); Iterator<String> iterGroups = entity.getGroups().iterator(); while (iterGroups.hasNext()) { String groupName = (String) iterGroups.next(); document.add(new StringField(CONTENT_GROUP_FIELD_NAME, groupName, Field.Store.YES)); } try { EntityAttributeIterator attributesIter = new EntityAttributeIterator(entity); while (attributesIter.hasNext()) { AttributeInterface currentAttribute = (AttributeInterface) attributesIter.next(); List<Lang> langs = this.getLangManager().getLangs(); for (int i=0; i<langs.size(); i++) { Lang currentLang = (Lang) langs.get(i); this.indexAttribute(document, currentAttribute, currentLang); } } List<Category> categories = entity.getCategories(); if (null != categories && !categories.isEmpty()) { for (int i = 0; i < categories.size(); i++) { Category category = categories.get(i); this.indexCategory(document, category); } } } catch (Throwable t) { _logger.error("Error creating document", t); throw new ApsSystemException("Error creating document", t); } return document; } private void indexCategory(Document document, Category categoryToIndex) { if (null == categoryToIndex || categoryToIndex.isRoot()) { return; } document.add(new StringField(CONTENT_CATEGORY_FIELD_NAME, categoryToIndex.getPath(CONTENT_CATEGORY_SEPARATOR, false), Field.Store.YES)); this.indexCategory(document, categoryToIndex.getParent()); } private void indexAttribute(Document document, AttributeInterface attribute, Lang lang) throws ApsSystemException { attribute.setRenderingLang(lang.getCode()); if (attribute instanceof IndexableAttributeInterface) { String valueToIndex = ((IndexableAttributeInterface) attribute).getIndexeableFieldValue(); String indexingType = attribute.getIndexingType(); String fieldName = lang.getCode(); if (attribute instanceof ResourceAttributeInterface) { fieldName += IIndexerDAO.ATTACHMENT_FIELD_SUFFIX; } if (null != indexingType && IndexableAttributeInterface.INDEXING_TYPE_UNSTORED.equalsIgnoreCase(indexingType)) { document.add(new TextField(fieldName, valueToIndex, Field.Store.NO)); } if (null != indexingType && IndexableAttributeInterface.INDEXING_TYPE_TEXT.equalsIgnoreCase(indexingType)) { document.add(new TextField(fieldName, valueToIndex, Field.Store.YES)); } } if (attribute.isSearchable()) { List<Lang> langs = new ArrayList<Lang>(); langs.add(lang); AttributeTracer tracer = new AttributeTracer(); tracer.setLang(lang); String name = tracer.getFormFieldName(attribute); List<AttributeSearchInfo> searchInfos = attribute.getSearchInfos(langs); if (null != searchInfos) { for (int i = 0; i < searchInfos.size(); i++) { AttributeSearchInfo info = searchInfos.get(i); Field field = null; if (null != info.getDate()) { field = new TextField(name, DateTools.timeToString(info.getDate().getTime(), DateTools.Resolution.MINUTE), Field.Store.YES); } else if (null != info.getBigDecimal()) { field = new IntField(name, info.getBigDecimal().intValue(), Field.Store.YES); } else { field = new TextField(name, info.getString(), Field.Store.YES); } document.add(field); } } } } /** * Cancella un documento. * @param name Il nome del campo Field da utilizzare per recupero del documento. * @param value La chiave mediante il quale รจ stato indicizzato il documento. * @throws ApsSystemException In caso di errore */ @Override public synchronized void delete(String name, String value) throws ApsSystemException { try { IndexWriter writer = new IndexWriter(this._dir, this.getIndexWriterConfig()); writer.deleteDocuments(new Term(name, value)); writer.close(); } catch (IOException e) { _logger.error("Error deleting document", e); throw new ApsSystemException("Error deleting document", e); } } @Override public void close() { // nothing to do } private Analyzer getAnalyzer() { return new StandardAnalyzer(); } private IndexWriterConfig getIndexWriterConfig() { return new IndexWriterConfig(Version.LUCENE_4_10_4, this.getAnalyzer()); } protected ILangManager getLangManager() { return _langManager; } @Override public void setLangManager(ILangManager langManager) { this._langManager = langManager; } private Directory _dir; private ILangManager _langManager; }