/*******************************************************************************
* * Copyright 2012 Impetus Infotech.
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
******************************************************************************/
package com.impetus.kundera.index;
import java.io.CharArrayReader;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.EmbeddableType;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.Tokenizer;
import org.apache.lucene.analysis.core.LetterTokenizer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.util.Version;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.impetus.kundera.metadata.model.EntityMetadata;
import com.impetus.kundera.metadata.model.MetamodelImpl;
import com.impetus.kundera.metadata.model.PropertyIndex;
import com.impetus.kundera.metadata.model.attributes.AbstractAttribute;
import com.impetus.kundera.property.PropertyAccessException;
import com.impetus.kundera.property.PropertyAccessorHelper;
import com.impetus.kundera.utils.KunderaCoreUtils;
/**
* The Class KunderaIndexer.
*
* @author animesh.kumar
*/
@SuppressWarnings(value = { "all" })
public abstract class DocumentIndexer implements com.impetus.kundera.index.lucene.Indexer
{
/** log for this class. */
private static final Logger LOG = LoggerFactory.getLogger(DocumentIndexer.class);
/** The INDEX_NAME. */
protected static final String INDEX_NAME = "kundera-alpha";// is
/** The Constant UUID. */
private static final long UUID = 6077004083174677888L;
/** The Constant DEFAULT_SEARCHABLE_FIELD. */
protected static final String DEFAULT_SEARCHABLE_FIELD = UUID + ".default_property";
/** The Constant SUPERCOLUMN_INDEX. */
protected static final String SUPERCOLUMN_INDEX = UUID + ".entity.super.indexname";
/** The doc number. */
protected static int docNumber = 1;
/** The analyzer. */
protected Analyzer analyzer;
/** The tokenizer. */
protected Tokenizer tokenizer;
/**
* Instantiates a new lucandra indexer.
*
* @param analyzer
* the analyzer
*/
public DocumentIndexer()
{
final String empty = "";
this.analyzer = new StandardAnalyzer(Version.LUCENE_CURRENT);
tokenizer = new LetterTokenizer(Version.LUCENE_34, new CharArrayReader(empty.toCharArray()));
}
/**
* Prepare document.
*
* @param metadata
* the metadata
* @param object
* the object
* @param embeddedColumnName
* the super column name
* @param parentId
* the parent id
* @param clazz
* the clazz
* @return the document
*/
protected Document prepareDocumentForSuperColumn(EntityMetadata metadata, Object object, String embeddedColumnName,
String parentId, Class<?> clazz)
{
Document currentDoc;
currentDoc = new Document();
// Add entity class and row key info to document
addEntityClassToDocument(metadata, object, currentDoc, null);
// Add super column name to document
addSuperColumnNameToDocument(embeddedColumnName, currentDoc);
addParentKeyToDocument(parentId, currentDoc, clazz);
return currentDoc;
}
/**
* Index parent key.
*
* @param parentId
* the parent id
* @param currentDoc
* the current doc
* @param clazz
* the clazz
*/
protected void addParentKeyToDocument(String parentId, Document currentDoc, Class<?> clazz)
{
if (parentId != null)
{
Field luceneField = new Field(IndexingConstants.PARENT_ID_FIELD, parentId, Field.Store.YES,
Field.Index.ANALYZED_NO_NORMS);
currentDoc.add(luceneField);
Field fieldClass = new Field(IndexingConstants.PARENT_ID_CLASS, clazz.getCanonicalName().toLowerCase(),
Field.Store.YES, Field.Index.ANALYZED);
currentDoc.add(fieldClass);
}
}
/**
* Index super column.
*
* @param metadata
* the metadata
* @param object
* the object
* @param currentDoc
* the current doc
* @param embeddedObject
* the embedded object
* @param superColumn
* the super column
* @param metamodel
*/
protected void createSuperColumnDocument(EntityMetadata metadata, Object object, Document currentDoc,
Object embeddedObject, EmbeddableType superColumn, MetamodelImpl metamodel)
{
// Add all super column fields into document
Set<Attribute> attributes = superColumn.getAttributes();
Iterator<Attribute> iter = attributes.iterator();
while (iter.hasNext())
{
Attribute attr = iter.next();
java.lang.reflect.Field field = (java.lang.reflect.Field) attr.getJavaMember();
String colName = field.getName();
String indexName = metadata.getIndexName();
addFieldToDocument(embeddedObject, currentDoc, field, colName, indexName);
}
// Add all entity fields to document
addEntityFieldsToDocument(metadata, object, currentDoc, metamodel);
}
/**
* Index super column.
*
* @param metadata
* the metadata
* @param object
* the object
* @param currentDoc
* the current doc
* @param embeddedObject
* the embedded object
* @param superColumn
* the super column
* @param metamodel
*/
protected void indexSuperColumn(EntityMetadata metadata, Object object, Document currentDoc, Object embeddedObject,
EmbeddableType superColumn, MetamodelImpl metamodel)
{
// Add all super column fields into document
Set<Attribute> attributes = superColumn.getAttributes();
Iterator<Attribute> iter = attributes.iterator();
while (iter.hasNext())
{
Attribute attr = iter.next();
java.lang.reflect.Field field = (java.lang.reflect.Field) attr.getJavaMember();
String colName = field.getName();
String indexName = metadata.getIndexName();
addFieldToDocument(embeddedObject, currentDoc, field, colName, indexName);
}
// Add all entity fields to document
addEntityFieldsToDocument(metadata, object, currentDoc, metamodel);
// Store document into Index
indexDocument(metadata, currentDoc);
}
/**
* Index super column name.
*
* @param superColumnName
* the super column name
* @param currentDoc
* the current doc
*/
private void addSuperColumnNameToDocument(String superColumnName, Document currentDoc)
{
Field luceneField = new Field(SUPERCOLUMN_INDEX, superColumnName, Store.YES, Field.Index.NO);
currentDoc.add(luceneField);
}
/**
* Adds the index properties.
*
* @param metadata
* the metadata
* @param entity
* the object
* @param document
* the document
* @param metaModel
*/
protected void addEntityFieldsToDocument(EntityMetadata metadata, Object entity, Document document,
MetamodelImpl metaModel)
{
String indexName = metadata.getIndexName();
Map<String, PropertyIndex> indexProperties = metadata.getIndexProperties();
for (String columnName : indexProperties.keySet())
{
PropertyIndex index = indexProperties.get(columnName);
java.lang.reflect.Field property = index.getProperty();
String propertyName = index.getName();
addFieldToDocument(entity, document, property, propertyName, indexName);
}
if (metaModel.isEmbeddable(metadata.getIdAttribute().getBindableJavaType()))
{
Object id = PropertyAccessorHelper.getId(entity, metadata);
indexCompositeKey(metadata, id, document, metaModel);
}
}
/**
* index compositekey
*
* @param metadata
* @param id
* @param document
* @param metaModel
*/
protected void indexCompositeKey(EntityMetadata metadata, Object id, Document document,
final MetamodelImpl metaModel)
{
// indexing individual fields of the composite key
EmbeddableType embeddableId = metaModel.embeddable(metadata.getIdAttribute().getBindableJavaType());
Set<Attribute> embeddedAttributes = embeddableId.getAttributes();
for (Attribute embeddedAttrib : embeddedAttributes)
{
String columnName = ((AbstractAttribute) embeddedAttrib).getJPAColumnName();
addFieldToDocument(id, document, (java.lang.reflect.Field) embeddedAttrib.getJavaMember(), columnName,
metadata.getEntityClazz().getSimpleName());
}
}
/**
* Prepare index document.
*
* @param metadata
* the metadata
* @param entity
* the object
* @param document
* the document
*/
protected void addEntityClassToDocument(EntityMetadata metadata, Object entity, Document document,
final MetamodelImpl metaModel)
{
try
{
Field luceneField;
Object id;
id = PropertyAccessorHelper.getId(entity, metadata);
// Indexing composite keys
if (metaModel != null && metaModel.isEmbeddable(metadata.getIdAttribute().getBindableJavaType()))
{
id = KunderaCoreUtils.prepareCompositeKey(metadata, metaModel, id);
}
luceneField = new Field(IndexingConstants.ENTITY_ID_FIELD, id.toString(), Field.Store.YES,
Field.Index.ANALYZED);
// luceneField.set
// adding class
// namespace
// /*Field.Store.YES, Field.Index.ANALYZED_NO_NORMS*/);
document.add(luceneField);
// index namespace for unique deletion
luceneField = new Field(IndexingConstants.KUNDERA_ID_FIELD, getKunderaId(metadata, id), Field.Store.YES,
Field.Index.ANALYZED); // adding
// class
// namespace
// Field.Store.YES/*, Field.Index.ANALYZED_NO_NORMS*/);
document.add(luceneField);
// index entity class
luceneField = new Field(IndexingConstants.ENTITY_CLASS_FIELD, metadata.getEntityClazz().getCanonicalName()
.toLowerCase(), Field.Store.YES, Field.Index.ANALYZED);
document.add(luceneField);
//
luceneField = new Field("timestamp", System.currentTimeMillis() + "", Field.Store.YES, Field.Index.NO);
document.add(luceneField);
// index index name
luceneField = new Field(IndexingConstants.ENTITY_INDEXNAME_FIELD, metadata.getIndexName(), Field.Store.NO,
Field.Index.ANALYZED_NO_NORMS);
document.add(luceneField);
luceneField = new Field(getCannonicalPropertyName(metadata.getEntityClazz().getSimpleName(),
((AbstractAttribute) metadata.getIdAttribute()).getJPAColumnName()), id.toString(),
Field.Store.YES, Field.Index.ANALYZED_NO_NORMS);
document.add(luceneField);
}
catch (PropertyAccessException e)
{
throw new IllegalArgumentException("Id could not be read from object " + entity);
}
}
/**
* Index field.
*
* @param object
* the object
* @param document
* the document
* @param field
* the field
* @param colName
* the col name
* @param indexName
* the index name
*/
private void addFieldToDocument(Object object, Document document, java.lang.reflect.Field field, String colName,
String indexName)
{
try
{
Object obj = PropertyAccessorHelper.getObject(object, field);
// String value = (obj == null) ? null : obj.toString();
if (obj != null)
{
Field luceneField = new Field(getCannonicalPropertyName(indexName, colName), obj.toString(),
Field.Store.YES, Field.Index.ANALYZED_NO_NORMS);
document.add(luceneField);
}
else
{
LOG.warn("value is null for field" + field.getName());
}
}
catch (PropertyAccessException e)
{
LOG.error("Error in accessing field, Caused by:" + e.getMessage());
throw new LuceneIndexingException("Error in accessing field:" + field.getName(), e);
}
}
@Override
public Map<String, Object> search(String query, Class<?> parentClass, EntityMetadata parentMetadata,
Class<?> childClass, EntityMetadata childMetadata, Object entityId, int start, int count)
{
return null;
}
/**
* Gets the kundera id.
*
* @param metadata
* the metadata
* @param id
* the id
*
* @return the kundera id
*/
protected String getKunderaId(EntityMetadata metadata, Object id)
{
return metadata.getEntityClazz().getCanonicalName() + IndexingConstants.DELIMETER + id;
}
/**
* Gets the cannonical property name.
*
* @param indexName
* the index name
* @param propertyName
* the property name
*
* @return the cannonical property name
*/
protected String getCannonicalPropertyName(String indexName, String propertyName)
{
return indexName + "." + propertyName;
}
/**
* Index document.
*
* @param metadata
* the metadata
* @param currentDoc
* the current doc
*/
protected abstract void indexDocument(EntityMetadata metadata, Document currentDoc);
}