Package org.exoplatform.services.jcr.impl.core.query.lucene

Source Code of org.exoplatform.services.jcr.impl.core.query.lucene.NodeIndexer

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.exoplatform.services.jcr.impl.core.query.lucene;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Calendar;
import java.util.List;

import javax.jcr.NamespaceException;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.ValueFormatException;

import org.exoplatform.services.log.Log;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;

import org.exoplatform.services.document.DocumentReader;
import org.exoplatform.services.document.DocumentReaderService;
import org.exoplatform.services.document.HandlerNotFoundException;
import org.exoplatform.services.jcr.core.ExtendedPropertyType;
import org.exoplatform.services.jcr.core.value.ExtendedValue;
import org.exoplatform.services.jcr.dataflow.ItemDataConsumer;
import org.exoplatform.services.jcr.datamodel.InternalQName;
import org.exoplatform.services.jcr.datamodel.NodeData;
import org.exoplatform.services.jcr.datamodel.PropertyData;
import org.exoplatform.services.jcr.datamodel.QPathEntry;
import org.exoplatform.services.jcr.datamodel.ValueData;
import org.exoplatform.services.jcr.impl.Constants;
import org.exoplatform.services.jcr.impl.core.LocationFactory;
import org.exoplatform.services.jcr.impl.core.value.ValueFactoryImpl;
import org.exoplatform.services.jcr.impl.dataflow.AbstractValueData;
import org.exoplatform.services.log.ExoLogger;

/**
* Creates a lucene <code>Document</code> object from a {@link javax.jcr.Node}.
*/
public class NodeIndexer
{

   /**
    * The logger instance for this class.
    */
   private static final Log log = ExoLogger.getLogger(NodeIndexer.class);

   /**
    * The default boost for a lucene field: 1.0f.
    */
   protected static final float DEFAULT_BOOST = 1.0f;

   /**
    * The <code>NodeState</code> of the node to index
    */
   protected final NodeData node;

   /**
    * The persistent item state provider
    */
   protected final ItemDataConsumer stateProvider;

   /**
    * Namespace mappings to use for indexing. This is the internal namespace
    * mapping.
    */
   protected final NamespaceMappings mappings;

   /**
    * Name and Path resolver.
    */
   protected final LocationFactory resolver;

   /**
    * Content extractor.
    */
   protected final DocumentReaderService extractor;

   /**
    * The indexing configuration or <code>null</code> if none is available.
    */
   protected IndexingConfiguration indexingConfig;

   /**
    * If set to <code>true</code> the fulltext field is stored and and a term
    * vector is created with offset information.
    */
   protected boolean supportHighlighting = false;

   /**
    * Indicates index format for this node indexer.
    */
   protected IndexFormatVersion indexFormatVersion = IndexFormatVersion.V1;

   private ValueFactoryImpl vFactory;

   /**
    * Creates a new node indexer.
    *
    * @param node the node state to index.
    * @param stateProvider the persistent item state manager to retrieve
    *          properties. //StateManager
    * @param mappings internal namespace mappings.
    * @param extractor content extractor
    */
   public NodeIndexer(NodeData node, ItemDataConsumer stateProvider, NamespaceMappings mappings,
            DocumentReaderService extractor)
   {
      this.node = node;
      this.stateProvider = stateProvider;
      this.mappings = mappings;
      this.resolver = new LocationFactory(mappings);
      this.extractor = extractor;
      this.vFactory = new ValueFactoryImpl(this.resolver);
   }

   /**
    * Returns the <code>NodeId</code> of the indexed node.
    *
    * @return the <code>NodeId</code> of the indexed node.
    */
   public String getNodeId()
   {
      return node.getIdentifier();
   }

   /**
    * If set to <code>true</code> additional information is stored in the index
    * to support highlighting using the exo:excerpt pseudo property.
    *
    * @param b <code>true</code> to enable highlighting support.
    */
   public void setSupportHighlighting(boolean b)
   {
      supportHighlighting = b;
   }

   /**
    * Sets the index format version
    *
    * @param indexFormatVersion the index format version
    */
   public void setIndexFormatVersion(IndexFormatVersion indexFormatVersion)
   {
      this.indexFormatVersion = indexFormatVersion;
   }

   /**
    * Sets the indexing configuration for this node indexer.
    *
    * @param config the indexing configuration.
    */
   public void setIndexingConfiguration(IndexingConfiguration config)
   {
      this.indexingConfig = config;
   }

   /**
    * Creates a lucene Document.
    *
    * @return the lucene Document with the index layout.
    * @throws RepositoryException if an error occurs while reading property
    *           values from the <code>ItemStateProvider</code>.
    */
   protected Document createDoc() throws RepositoryException
   {
      Document doc = new Document();

      doc.setBoost(getNodeBoost());

      // special fields UUID
      doc.add(new Field(FieldNames.UUID, node.getIdentifier(), Field.Store.YES, Field.Index.NO_NORMS,
               Field.TermVector.NO));
      try
      {
         // parent UUID
         if (node.getParentIdentifier() == null)
         {
            // root node
            doc.add(new Field(FieldNames.PARENT, "", Field.Store.YES, Field.Index.NO_NORMS, Field.TermVector.NO));
            doc.add(new Field(FieldNames.LABEL, "", Field.Store.YES, Field.Index.NO_NORMS, Field.TermVector.NO));
         }
         else
         {
            doc.add(new Field(FieldNames.PARENT, node.getParentIdentifier(), Field.Store.YES, Field.Index.NO_NORMS,
                     Field.TermVector.NO));

            String name = resolver.createJCRName(node.getQPath().getName()).getAsString();
            doc.add(new Field(FieldNames.LABEL, name, Field.Store.YES, Field.Index.NO_NORMS, Field.TermVector.NO));
         }
      }
      catch (NamespaceException e)
      {
         // will never happen, because this.mappings will dynamically add
         // unknown uri<->prefix mappings
      }

      for (PropertyData prop : stateProvider.listChildPropertiesData(node))
      {
         // test the case and switch to

         // add each property to the _PROPERTIES_SET for searching beginning with
         // V2
         if (indexFormatVersion.getVersion() >= IndexFormatVersion.V2.getVersion())
         {
            addPropertyName(doc, prop.getQPath().getName());
         }

         addValues(doc, prop);
      }
      return doc;
   }

   /**
    * Adds a {@link FieldNames#MVP} field to <code>doc</code> with the resolved
    * <code>name</code> using the internal search index namespace mapping.
    *
    * @param doc the lucene document.
    * @param name the name of the multi-value property.
    */
   private void addMVPName(Document doc, InternalQName name) throws RepositoryException
   {
      String propName = resolver.createJCRName(name).getAsString();
      doc.add(new Field(FieldNames.MVP, propName, Field.Store.NO, Field.Index.UN_TOKENIZED, Field.TermVector.NO));
   }

   /**
    * For concurrency usage case all internal vars were made a final. Need to be
    * rechecked.
    */
   private void addValues(final Document doc, final PropertyData prop) throws RepositoryException
   {

      int propType = prop.getType();
      String fieldName = resolver.createJCRName(prop.getQPath().getName()).getAsString();
      if (propType == PropertyType.BINARY)
      {

         List<ValueData> data = null;
         if (node.getQPath().getName().equals(Constants.JCR_CONTENT))
         {

            // seems nt:file found, try for nt:resource props
            PropertyData pmime =
                     (PropertyData) stateProvider.getItemData(node, new QPathEntry(Constants.JCR_MIMETYPE, 0));
            if (pmime != null)
            {
               // index if have jcr:mimeType sibling for this binary property only
               try
               {
                  DocumentReader dreader =
                           extractor.getDocumentReader(new String(pmime.getValues().get(0).getAsByteArray()));

                  // ok, have a reader
                  // if the prop obtainer from cache it will contains a values,
                  // otherwise read prop with values from DM
                  data =
                           prop.getValues().size() > 0 ? prop.getValues() : ((PropertyData) stateProvider.getItemData(
                                    node, new QPathEntry(Constants.JCR_DATA, 0))).getValues();
                  if (data == null)
                     log.warn("null value found at property " + prop.getQPath().getAsString());

                  // check the jcr:encoding property
                  PropertyData encProp =
                           (PropertyData) stateProvider.getItemData(node, new QPathEntry(Constants.JCR_ENCODING, 0));

                  if (encProp != null)
                  {
                     // encoding parameter used
                     String encoding = new String(encProp.getValues().get(0).getAsByteArray());
                     for (ValueData pvd : data)
                     {
                        InputStream is = null;
                        try
                        {
                           doc.add(createFulltextField(dreader.getContentAsText(is = pvd.getAsStream(), encoding)));
                        }
                        finally
                        {
                           try
                           {
                              is.close();
                           }
                           catch (Throwable e)
                           {
                           }
                        }
                     }
                  }
                  else
                  {
                     // no encoding parameter
                     for (ValueData pvd : data)
                     {
                        InputStream is = null;
                        try
                        {
                           doc.add(createFulltextField(dreader.getContentAsText(is = pvd.getAsStream())));
                        }
                        finally
                        {
                           try
                           {
                              is.close();
                           }
                           catch (Throwable e)
                           {
                           }
                        }
                     }
                  }

                  if (data.size() > 1)
                  {
                     // real multi-valued
                     addMVPName(doc, prop.getQPath().getName());
                  }

               }
               catch (HandlerNotFoundException e)
               {
                  // no handler - no index
                  if (log.isDebugEnabled())
                     log.warn("This content is not readable " + e);
               }
               catch (IOException e)
               {
                  // no data - no index
                  if (log.isDebugEnabled())
                     log.warn("Binary value indexer IO error " + e, e);
               }
               catch (Exception e)
               {
                  log.error("Binary value indexer error " + e, e);
               }
            }
         }

      }
      else
      {
         try
         {
            // if the prop obtainer from cache it will contains a values, otherwise
            // read prop with values from DM
            // WARN. DON'T USE access item BY PATH - it's may be a node in case of
            // residual definitions in NT
            List<ValueData> data =
                     prop.getValues().size() > 0 ? prop.getValues() : ((PropertyData) stateProvider.getItemData(prop
                              .getIdentifier())).getValues();

            if (data == null)
               log.warn("null value found at property " + prop.getQPath().getAsString());

            ExtendedValue val = null;
            InternalQName name = prop.getQPath().getName();

            for (ValueData value : data)
            {
               val = (ExtendedValue) vFactory.loadValue(((AbstractValueData) value).createTransientCopy(), propType);

               switch (propType)
               {
                  case PropertyType.BOOLEAN :
                     if (isIndexed(name))
                     {
                        addBooleanValue(doc, fieldName, Boolean.valueOf(val.getBoolean()));
                     }
                     break;
                  case PropertyType.DATE :
                     if (isIndexed(name))
                     {
                        addCalendarValue(doc, fieldName, val.getDate());
                     }
                     break;
                  case PropertyType.DOUBLE :
                     if (isIndexed(name))
                     {
                        addDoubleValue(doc, fieldName, new Double(val.getDouble()));
                     }
                     break;
                  case PropertyType.LONG :
                     if (isIndexed(name))
                     {
                        addLongValue(doc, fieldName, new Long(val.getLong()));
                     }
                     break;
                  case PropertyType.REFERENCE :
                     if (isIndexed(name))
                     {
                        addReferenceValue(doc, fieldName, val.getString());
                     }
                     break;
                  case PropertyType.PATH :
                     if (isIndexed(name))
                     {
                        addPathValue(doc, fieldName, val.getString());
                     }
                     break;
                  case PropertyType.STRING :
                     if (isIndexed(name))
                     {
                        // never fulltext index jcr:uuid String
                        if (name.equals(Constants.JCR_UUID))
                        {
                           addStringValue(doc, fieldName, val.getString(), false, false, DEFAULT_BOOST);
                        }
                        else
                        {
                           addStringValue(doc, fieldName, val.getString(), true, isIncludedInNodeIndex(name),
                                    getPropertyBoost(name));
                        }
                     }
                     break;
                  case PropertyType.NAME :
                     // jcr:primaryType and jcr:mixinTypes are required for correct
                     // node type resolution in queries
                     if (isIndexed(name) || name.equals(Constants.JCR_PRIMARYTYPE)
                              || name.equals(Constants.JCR_MIXINTYPES))
                     {
                        addNameValue(doc, fieldName, val.getString());
                     }
                     break;
                  case ExtendedPropertyType.PERMISSION :
                     break;
                  default :
                     throw new IllegalArgumentException("illegal internal value type " + propType);
               }
            }
            if (data.size() > 1)
               // real multi-valued
               addMVPName(doc, prop.getQPath().getName());
         }
         catch (RepositoryException e)
         {
            e.printStackTrace();
            throw new RepositoryException("Index of property value error. " + prop.getQPath().getAsString() + ". " + e,
                     e);
         }
      }
   }

   /**
    * Adds a value to the lucene Document.
    *
    * @param doc the document.
    * @param value the internal jackrabbit value.
    * @param name the name of the property.
    * @param
    * @throws RepositoryException
    * @throws IllegalStateException
    * @throws ValueFormatException
    */
   @Deprecated
   private void addValue(Document doc, ValueData value, InternalQName name, int propertyType)
            throws ValueFormatException, IllegalStateException, RepositoryException
   {
      String fieldName = resolver.createJCRName(name).getAsString();
      ExtendedValue val = null;
      if (PropertyType.BINARY != propertyType)
         val = (ExtendedValue) vFactory.loadValue(((AbstractValueData) value).createTransientCopy(), propertyType);
      switch (propertyType)
      {
         case PropertyType.BINARY :
            if (isIndexed(name))
            {
               addBinaryValue(doc, fieldName, value);
            }
            break;
         case PropertyType.BOOLEAN :
            if (isIndexed(name))
            {

               addBooleanValue(doc, fieldName, Boolean.valueOf(val.getBoolean()));
            }
            break;
         case PropertyType.DATE :
            if (isIndexed(name))
            {
               addCalendarValue(doc, fieldName, val.getDate());
            }
            break;
         case PropertyType.DOUBLE :
            if (isIndexed(name))
            {
               addDoubleValue(doc, fieldName, new Double(val.getDouble()));
            }
            break;
         case PropertyType.LONG :
            if (isIndexed(name))
            {
               addLongValue(doc, fieldName, new Long(val.getLong()));
            }
            break;
         case PropertyType.REFERENCE :
            if (isIndexed(name))
            {
               addReferenceValue(doc, fieldName, val.getString());
            }
            break;
         case PropertyType.PATH :
            if (isIndexed(name))
            {
               addPathValue(doc, fieldName, val.getString());
            }
            break;
         case PropertyType.STRING :
            if (isIndexed(name))
            {
               // never fulltext index jcr:uuid String
               if (name.equals(Constants.JCR_UUID))
               {
                  addStringValue(doc, fieldName, val.getString(), false, false, DEFAULT_BOOST);
               }
               else
               {
                  addStringValue(doc, fieldName, val.getString(), true, isIncludedInNodeIndex(name),
                           getPropertyBoost(name));
               }
            }
            break;
         case PropertyType.NAME :
            // jcr:primaryType and jcr:mixinTypes are required for correct
            // node type resolution in queries
            if (isIndexed(name) || name.equals(Constants.JCR_PRIMARYTYPE) || name.equals(Constants.JCR_MIXINTYPES))
            {
               addNameValue(doc, fieldName, val.getString());
            }
            break;
         case ExtendedPropertyType.PERMISSION :
            break;
         default :
            throw new IllegalArgumentException("illegal internal value type:"
                     + ExtendedPropertyType.nameFromValue(propertyType));
      }
   }

   /**
    * Adds the property name to the lucene _:PROPERTIES_SET field.
    *
    * @param doc the document.
    * @param name the name of the property.
    */
   private void addPropertyName(Document doc, InternalQName name) throws RepositoryException
   {
      String fieldName = resolver.createJCRName(name).getAsString();
      doc.add(new Field(FieldNames.PROPERTIES_SET, fieldName, Field.Store.YES, Field.Index.NO_NORMS));
   }

   /**
    * Adds the binary value to the document as the named field. <p/> This
    * implementation checks if this {@link #node} is of type nt:resource and if
    * that is the case, tries to extract text from the binary property using the
    * {@link #extractor}.
    *
    * @param doc The document to which to add the field
    * @param fieldName The name of the field to add
    * @param internalValue The value for the field to add to the document.
    * @throws RepositoryException
    */
   @Deprecated
   private void addBinaryValue(Document doc, String fieldName, ValueData internalValue) throws RepositoryException
   {

      if (node.getQPath().getName().equals(Constants.JCR_CONTENT))
      {
         String text;
         try
         {
            PropertyData prop =
                     (PropertyData) stateProvider.getItemData(node, new QPathEntry(Constants.JCR_MIMETYPE, 0));
            if (prop != null)
            {
               List<ValueData> values = prop.getValues();
               ValueData mimeValue = values.get(0);
               String mime = new String(mimeValue.getAsByteArray());

               DocumentReader dreader = extractor.getDocumentReader(mime);

               InputStream is = null;
               try
               {
                  is = internalValue.getAsStream();

                  // check the jcr:encoding property
                  PropertyData encProp =
                           (PropertyData) stateProvider.getItemData(node, new QPathEntry(Constants.JCR_ENCODING, 0));
                  if (encProp != null)
                  {
                     ValueData encValue = encProp.getValues().get(0);
                     text = dreader.getContentAsText(is, new String(encValue.getAsByteArray()));
                  }
                  else
                  {
                     text = dreader.getContentAsText(is);
                  }
               }
               finally
               {
                  try
                  {
                     is.close();
                  }
                  catch (Throwable e)
                  {
                  }
               }
            }
            else
               // non mimetype - no index, jcr:mimeType is mandatory for nt:resource,
               // but jcr:content type is nt:base, i.e. any nodetype can be there
               text = null;
         }
         catch (HandlerNotFoundException e)
         {
            // no handler - no index
            if (log.isDebugEnabled())
               log.warn("This content is not readable " + e);
            text = null;
         }
         catch (IOException e)
         {
            // no data - no index
            if (log.isDebugEnabled())
               log.warn("Binary value indexer IO error " + e, e);
            text = null;
         }
         catch (Exception e)
         {
            log.error("Binary value indexer error " + e, e);
            text = null;
         }

         if (text != null)
         {
            doc.add(createFulltextField(text));
         } // else log.warn("Properties binary value is not extracted");
      }
   }

   /**
    * Adds the string representation of the boolean value to the document as the
    * named field.
    *
    * @param doc The document to which to add the field
    * @param fieldName The name of the field to add
    * @param internalValue The value for the field to add to the document.
    */
   protected void addBooleanValue(Document doc, String fieldName, Object internalValue)
   {
      doc.add(createFieldWithoutNorms(fieldName, internalValue.toString(), false));
   }

   /**
    * Creates a field of name <code>fieldName</code> with the value of <code>
    * internalValue</code>
    * . The created field is indexed without norms.
    *
    * @param fieldName The name of the field to add
    * @param internalValue The value for the field to add to the document.
    * @param store <code>true</code> if the value should be stored,
    *          <code>false</code> otherwise
    */
   protected Field createFieldWithoutNorms(String fieldName, String internalValue, boolean store)
   {
      Field field =
               new Field(FieldNames.PROPERTIES, FieldNames.createNamedValue(fieldName, internalValue), true
                        ? Field.Store.YES : Field.Store.NO, Field.Index.NO_NORMS, Field.TermVector.NO);
      return field;
   }

   /**
    * Adds the calendar value to the document as the named field. The calendar
    * value is converted to an indexable string value using the {@link DateField}
    * class.
    *
    * @param doc The document to which to add the field
    * @param fieldName The name of the field to add
    * @param internalValue The value for the field to add to the document.
    */
   protected void addCalendarValue(Document doc, String fieldName, Object internalValue)
   {
      Calendar value = (Calendar) internalValue;
      long millis = value.getTimeInMillis();
      doc.add(createFieldWithoutNorms(fieldName, DateField.timeToString(millis), false));
   }

   /**
    * Adds the double value to the document as the named field. The double value
    * is converted to an indexable string value using the {@link DoubleField}
    * class.
    *
    * @param doc The document to which to add the field
    * @param fieldName The name of the field to add
    * @param internalValue The value for the field to add to the document.
    */
   protected void addDoubleValue(Document doc, String fieldName, Object internalValue)
   {
      double doubleVal = ((Double) internalValue).doubleValue();
      doc.add(createFieldWithoutNorms(fieldName, DoubleField.doubleToString(doubleVal), false));
   }

   /**
    * Adds the long value to the document as the named field. The long value is
    * converted to an indexable string value using the {@link LongField} class.
    *
    * @param doc The document to which to add the field
    * @param fieldName The name of the field to add
    * @param internalValue The value for the field to add to the document.
    */
   protected void addLongValue(Document doc, String fieldName, Object internalValue)
   {
      long longVal = ((Long) internalValue).longValue();
      doc.add(createFieldWithoutNorms(fieldName, LongField.longToString(longVal), false));
   }

   /**
    * Adds the reference value to the document as the named field. The value's
    * string representation is added as the reference data. Additionally the
    * reference data is stored in the index.
    *
    * @param doc The document to which to add the field
    * @param fieldName The name of the field to add
    * @param internalValue The value for the field to add to the document.
    */
   protected void addReferenceValue(Document doc, String fieldName, Object internalValue)
   {
      doc.add(createFieldWithoutNorms(fieldName, internalValue.toString(), true));
   }

   /**
    * Adds the path value to the document as the named field. The path value is
    * converted to an indexable string value using the name space mappings with
    * which this class has been created.
    *
    * @param doc The document to which to add the field
    * @param fieldName The name of the field to add
    * @param internalValue The value for the field to add to the document.
    */
   protected void addPathValue(Document doc, String fieldName, Object internalValue)
   {
      doc.add(createFieldWithoutNorms(fieldName, internalValue.toString(), false));
   }

   /**
    * Adds the string value to the document both as the named field and
    * optionally for full text indexing if <code>tokenized</code> is
    * <code>true</code>.
    *
    * @param doc The document to which to add the field
    * @param fieldName The name of the field to add
    * @param internalValue The value for the field to add to the document.
    * @param tokenized If <code>true</code> the string is also tokenized and
    *          fulltext indexed.
    */
   protected void addStringValue(Document doc, String fieldName, Object internalValue, boolean tokenized)
   {
      addStringValue(doc, fieldName, internalValue, tokenized, true, DEFAULT_BOOST);
   }

   /**
    * Adds the string value to the document both as the named field and
    * optionally for full text indexing if <code>tokenized</code> is
    * <code>true</code>.
    *
    * @param doc The document to which to add the field
    * @param fieldName The name of the field to add
    * @param internalValue The value for the field to add to the document.
    * @param tokenized If <code>true</code> the string is also tokenized and
    *          fulltext indexed.
    * @param includeInNodeIndex If <code>true</code> the string is also tokenized
    *          and added to the node scope fulltext index.
    * @param boost the boost value for this string field.
    */
   protected void addStringValue(Document doc, String fieldName, Object internalValue, boolean tokenized,
            boolean includeInNodeIndex, float boost)
   {
      // simple String
      String stringValue = (String) internalValue;
      doc.add(createFieldWithoutNorms(fieldName, stringValue, false));
      if (tokenized)
      {
         if (stringValue.length() == 0)
         {
            return;
         }
         // create fulltext index on property
         Field f =
                  new Field(FieldNames.createFullTextFieldName(fieldName), stringValue, Field.Store.NO,
                           Field.Index.TOKENIZED, Field.TermVector.NO);
         f.setBoost(boost);
         doc.add(f);

         if (includeInNodeIndex)
         {
            // also create fulltext index of this value
            doc.add(createFulltextField(stringValue));
         }
      }
   }

   /**
    * Adds the name value to the document as the named field. The name value is
    * converted to an indexable string treating the internal value as a qualified
    * name and mapping the name space using the name space mappings with which
    * this class has been created.
    *
    * @param doc The document to which to add the field
    * @param fieldName The name of the field to add
    * @param internalValue The value for the field to add to the document.
    */
   protected void addNameValue(Document doc, String fieldName, Object internalValue)
   {
      doc.add(createFieldWithoutNorms(fieldName, internalValue.toString(), false));
   }

   /**
    * Creates a fulltext field for the string <code>value</code>.
    *
    * @param value the string value.
    * @return a lucene field.
    */
   protected Field createFulltextField(String value)
   {
      if (supportHighlighting)
      {
         // store field compressed if greater than 16k
         Field.Store stored = Field.Store.YES;
         // TODO make the stored parameter be configurable. COMPRESS or only
         // Store.YES
         // if (value.length() > 0x4000) {
         // stored = Field.Store.COMPRESS;
         // } else {
         // stored = Field.Store.YES;
         // }

         return new Field(FieldNames.FULLTEXT, value, stored, Field.Index.TOKENIZED, Field.TermVector.WITH_OFFSETS);
      }
      else
      {

         return new Field(FieldNames.FULLTEXT, value, Field.Store.NO, Field.Index.TOKENIZED);
      }
   }

   /**
    * Creates a fulltext field for the reader <code>value</code>.
    *
    * @param value the reader value.
    * @return a lucene field.
    */
   protected Field createFulltextField(Reader value)
   {
      if (supportHighlighting)
      {
         // need to create a string value
         StringBuffer textExtract = new StringBuffer();
         char[] buffer = new char[1024];
         int len;
         try
         {
            while ((len = value.read(buffer)) > -1)
            {
               textExtract.append(buffer, 0, len);
            }
         }
         catch (IOException e)
         {
            log.warn("Exception reading value for fulltext field: " + e.getMessage());
            log.debug("Dump:", e);
         }
         finally
         {
            try
            {
               value.close();
            }
            catch (IOException e)
            {
               // ignore
            }
         }
         return createFulltextField(textExtract.toString());
      }
      else
      {
         return new Field(FieldNames.FULLTEXT, value);
      }
   }

   /**
    * Returns <code>true</code> if the property with the given name should be
    * indexed.
    *
    * @param propertyName name of a property.
    * @return <code>true</code> if the property should be fulltext indexed;
    *         <code>false</code> otherwise.
    */
   protected boolean isIndexed(InternalQName propertyName)
   {
      if (indexingConfig == null)
      {
         return true;
      }
      else
      {
         return indexingConfig.isIndexed(node, propertyName);
      }
   }

   /**
    * Returns <code>true</code> if the property with the given name should also
    * be added to the node scope index.
    *
    * @param propertyName the name of a property.
    * @return <code>true</code> if it should be added to the node scope index;
    *         <code>false</code> otherwise.
    */
   protected boolean isIncludedInNodeIndex(InternalQName propertyName)
   {
      if (indexingConfig == null)
      {
         return true;
      }
      else
      {
         return indexingConfig.isIncludedInNodeScopeIndex(node, propertyName);
      }
   }

   /**
    * Returns the boost value for the given property name.
    *
    * @param propertyName the name of a property.
    * @return the boost value for the given property name.
    */
   protected float getPropertyBoost(InternalQName propertyName)
   {
      if (indexingConfig == null)
      {
         return DEFAULT_BOOST;
      }
      else
      {
         return indexingConfig.getPropertyBoost(node, propertyName);
      }
   }

   /**
    * @return the boost value for this {@link #node} state.
    */
   protected float getNodeBoost()
   {
      if (indexingConfig == null)
      {
         return DEFAULT_BOOST;
      }
      else
      {
         return indexingConfig.getNodeBoost(node);
      }
   }
}
TOP

Related Classes of org.exoplatform.services.jcr.impl.core.query.lucene.NodeIndexer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.