Java tutorial
/*********************************************************************************************************************** * Copyright (c) 2008 empolis GmbH and brox IT Solutions GmbH. All rights reserved. This program and the accompanying * materials are made available under the terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html * * Contributors: August Georg Schmidt (brox IT Solutions GmbH) - initial API and implementation **********************************************************************************************************************/ package org.eclipse.smila.search.index; // standard utility classes import java.util.Collection; import java.util.Enumeration; import java.util.Hashtable; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.smila.blackboard.Blackboard; import org.eclipse.smila.blackboard.BlackboardAccessException; import org.eclipse.smila.datamodel.Any; import org.eclipse.smila.datamodel.AnyMap; import org.eclipse.smila.datamodel.AnySeq; import org.eclipse.smila.datamodel.DataFactory; import org.eclipse.smila.datamodel.Record; import org.eclipse.smila.datamodel.Value; import org.eclipse.smila.search.api.SearchResultConstants; import org.eclipse.smila.search.datadictionary.DataDictionaryController; import org.eclipse.smila.search.datadictionary.DataDictionaryException; import org.eclipse.smila.search.datadictionary.messages.datadictionary.DIndex; import org.eclipse.smila.search.datadictionary.messages.ddconfig.DConfiguration; import org.eclipse.smila.search.datadictionary.messages.ddconfig.DFieldConfig; import org.eclipse.smila.search.datadictionary.messages.ddconfig.DFieldConstraints; import org.eclipse.smila.search.datadictionary.messages.ddconfig.DNamedConfig; import org.eclipse.smila.search.datadictionary.messages.ddconfig.DQueryConstraints; import org.eclipse.smila.search.templates.NodeTransformerException; import org.eclipse.smila.search.templates.TemplateException; import org.eclipse.smila.search.templates.TemplateRegistryController; import org.eclipse.smila.search.templates.messages.searchtemplates.DTemplate; import org.eclipse.smila.search.utils.advsearch.IQueryExpression; import org.eclipse.smila.search.utils.indexstructure.DIndexField; import org.eclipse.smila.search.utils.search.DField; import org.eclipse.smila.search.utils.search.DQuery; import org.eclipse.smila.search.utils.search.DTextField; import org.eclipse.smila.search.utils.search.IDFParameter; import org.eclipse.smila.search.utils.search.INFParameter; import org.eclipse.smila.search.utils.search.ITFParameter; import org.eclipse.smila.search.utils.searchresult.LuceneSearchResult; /** * A Class class. * <P> * * @author BROX IT-Solutions GmbH */ public abstract class IndexConnection { /** * Index configuration from data dictionary. */ protected DIndex _index; /** * Index name. */ protected String _indexName; /** * @param indexName * name of index to be created * @throws IndexException * Unable to create index connection. */ protected IndexConnection(final String indexName) throws IndexException { try { this._index = DataDictionaryController.getIndex(indexName); } catch (final DataDictionaryException e) { throw new IndexException(e.getMessage(), e); } if (this._index == null) { throw new IndexException("index not in data dictionary [" + indexName + "]"); } this._indexName = indexName; } /** * Get index name. * * @return Index name. */ public String getName() { return this._indexName; } /** * Get index configuration definition from data dictionary. * * @return Index configuration definition from data dictionary. */ public DIndex getIndex() { return this._index; } /** * Close index connection. */ protected abstract void close(); /** * Checks whether a document exists in index. * * @param id * the Id * @return Whether a document exists. * @throws IndexException * Unable to check the document exists in index. */ public abstract boolean docExists(String id) throws IndexException; /** * Delete document from index. * * @param id * the Id * @throws IndexException * Unable to delete document from index. */ public abstract void deleteDocument(String id) throws IndexException; /** * Add a document to index. * * @param blackboard * the BlackboardService * @param id * the Id of the record to add. * @param attributeMapping * Map containing the attribute to FieldNo mapping * @param attachmentMapping * Map containing the attachment to FieldNo mapping * @throws IndexException * Unable to add document to index. * @throws BlackboardAccessException * if errors occur accessing the Blackboard */ public abstract void learnDocument(Blackboard blackboard, String id, Map<String, Integer> attributeMapping, Map<String, Integer> attachmentMapping) throws IndexException, BlackboardAccessException; /** * Start burst mode. * * @throws IndexException * Unable to start burst mode. */ protected abstract void startBurstmode() throws IndexException; /** * Stop burst mode. * * @throws IndexException * Unable to stop burst mode. */ protected abstract void stopBurstmode() throws IndexException; /** * Transform a simple search to the corresponding advanced search. * * @param dQuery * Simple search. * @return Advanced search. * @throws IndexException * Unable to get advanced search. * @throws NodeTransformerException * Unable to perform node transformation. * @throws TemplateException * Unable to apply template. */ public abstract IQueryExpression getSimpleSearchQuery(DQuery dQuery) throws IndexException, NodeTransformerException, TemplateException; /** * Execute search query. * * @param dQE * Advanced search query. * @param startPos * Start position. * @return Result. * @throws IndexException * Unable to perform search query. */ protected abstract LuceneSearchResult doQuery(IQueryExpression dQE, int startPos) throws IndexException; /** * @param text * - * @return IQueryExpression * @throws IndexException * - */ protected abstract IQueryExpression getTestQueryExpression(String text) throws IndexException; /** * @param field * - * @param configParameter * - * @param config * - * @param sb * - */ protected abstract void insertTextParameter(org.eclipse.smila.search.utils.search.DTextField field, ITFParameter configParameter, String config, StringBuffer sb); /** * @param field * - * @param configParameter * - * @param config * - * @param sb * - */ protected abstract void insertNumberParameter(org.eclipse.smila.search.utils.search.DNumberField field, INFParameter configParameter, String config, StringBuffer sb); /** * @param field * - * @param configParameter * - * @param config * - * @param sb * - */ protected abstract void insertDateParameter(org.eclipse.smila.search.utils.search.DDateField field, IDFParameter configParameter, String config, StringBuffer sb); /** * @param keys * - * @param fields * - * @param values * - * @throws IndexException * - */ protected abstract void getResultValues(String[] keys, int[] fields, String[][] values) throws IndexException; protected abstract String[] getResultValues(final String id, final int fieldNo) throws IndexException; /** * @param tf * - * @throws IndexException * - */ protected abstract void encodeTextField(DTextField tf) throws IndexException; /** * @param fieldNo * - * @param value * - * @throws IndexException * - */ protected abstract void isSupportedValue(int fieldNo, Object value) throws IndexException; /** * This method extends a given search query with all possible configurable attributes. Additionally checks this method * wether there are any conflicts with the index structure or the query constraints. * * @param dQuery * - * @throws IndexException * - */ public void validateQuery(final DQuery dQuery) throws IndexException { final Log log = LogFactory.getLog(getClass()); final Hashtable<Integer, DField> queryFields = new Hashtable<Integer, DField>(); final DConfiguration dConfig = _index.getConfiguration(); // 1. check fields of query against index. // 2. apply configuration settings Enumeration fields = dQuery.getFields(); while (fields.hasMoreElements()) { final DField field = (DField) fields.nextElement(); if (!_index.getIndexStructure().hasField(field.getFieldNo())) { throw new IndexException( "field in search query does not exist in index [" + field.getFieldNo() + "]"); } // checks whether types of search field and index field are compatible. // for checking, the type taken from the index field's default configuration will be used, // because there we already have simple search types, whereas in the index structure, // different types may be used (e. g. Date may be represented by Number) if (!dConfig.getDefaultConfig().getField(field.getFieldNo()).getFieldConfig().getType() .equals(field.getType())) { throw new IndexException("field type in search query does not match field type in index [" + field.getFieldNo() + "]"); } // get default whole parameterization DFieldConfig fc = null; // check for named config if (field.getParameterDescriptor() != null) { DNamedConfig nc = null; if (!dConfig.hasNamedConfig(field.getParameterDescriptor())) { log.error("unable to locate named config for index [" + dQuery.getIndexName() + ";" + field.getParameterDescriptor() + "]"); } else { nc = dConfig.getNamedConfig(field.getParameterDescriptor()); if (nc != null) { // check for field type specifig named config fc = nc.getFieldConfig(field); if (fc == null) { log.error("unable to locate named config for index [" + dQuery.getIndexName() + ";" + field.getParameterDescriptor() + ";" + field.getType() + "]"); } } } } final DFieldConfig defaultfc = dConfig.getDefaultConfig().getField(field.getFieldNo()).getFieldConfig(); // set default config applyFieldConfig(field, fc, defaultfc); queryFields.put(new Integer(field.getFieldNo()), field); } // 3. check query constraints for simple search. final DQueryConstraints queryConstraints = dConfig.getQueryConstraints(); if (queryConstraints != null) { for (int i = 0; i < queryConstraints.getFieldConstraintsLength(); i++) { final DFieldConstraints dFieldConstraints = queryConstraints.getFieldConstraints(i); final String occurrence = dFieldConstraints.getOccurrence(); if (occurrence.equals("required")) { if (!queryFields.containsKey(new Integer(dFieldConstraints.getFieldNo()))) { throw new IndexException( "query constraint requires field [" + dFieldConstraints.getFieldNo() + "]"); } } else if (occurrence.equals("prohibited")) { if (queryFields.containsKey(new Integer(dFieldConstraints.getFieldNo()))) { throw new IndexException( "query constraint prohibits field [" + dFieldConstraints.getFieldNo() + "]"); } } // check further query constraints final DField field = queryFields.get(new Integer(dFieldConstraints.getFieldNo())); if (field == null) { continue; } if (dFieldConstraints.getFieldTemplateCount() > 0) { boolean matchConstraint = false; final String[] fieldTemplates = dFieldConstraints.getFieldTemplates(); final String fieldTemplate = field.getFieldTemplate() != null ? field.getFieldTemplate().trim() : ""; for (final String fieldTemplate2 : fieldTemplates) { if (fieldTemplate2.equals(fieldTemplate)) { matchConstraint = true; break; } } if (!matchConstraint) { throw new IndexException("query field does not match field template constraint [" + field.getFieldNo() + "]"); } } if (dFieldConstraints.getNodeTransformerCount() > 0) { boolean matchConstraint = false; final String[] nodeTransformers = dFieldConstraints.getNodeTransformers(); final String nodeTransformerName = field.getNodeTransformer() != null ? field.getNodeTransformer().getName() : ""; for (final String nodeTransformer : nodeTransformers) { if (nodeTransformer.equals(nodeTransformerName)) { matchConstraint = true; break; } } if (!matchConstraint) { throw new IndexException("query field does not match node transformer constraint [" + field.getFieldNo() + "]"); } } if (dFieldConstraints.getConstraintCount() > 0) { boolean matchConstraint = false; final String[] constraints = dFieldConstraints.getConstraints(); final String constraint = field.getConstraint() != null ? field.getConstraint() : ""; for (final String constraint2 : constraints) { if (constraint2.equals(constraint)) { matchConstraint = true; break; } } if (!matchConstraint) { throw new IndexException( "query field does not match constraint constraint [" + field.getFieldNo() + "]"); } } } } // apply field valiadtion and transformation fields = dQuery.getFields(); while (fields.hasMoreElements()) { final DField field = (DField) fields.nextElement(); if (field instanceof DTextField) { encodeTextField((DTextField) field); } } } public LuceneSearchResult doQuery(final IQueryExpression dQE) throws IndexException { final int startPos = dQE.getStartHits() == null ? 0 : dQE.getStartHits().intValue(); final LuceneSearchResult result = doQuery(dQE, startPos); return result; } /** * Performs a search for a fully complex query expression. * * @param dQuery * - * @return LuceneSearchResult * @throws IndexException * - */ public LuceneSearchResult doQuery(final DQuery dQuery) throws IndexException { final Log log = LogFactory.getLog(getClass()); validateQuery(dQuery); try { final DTemplate dTemplate = TemplateRegistryController.getTemplate(dQuery); IQueryExpression dQE = null; if (dTemplate != null) { if (log.isInfoEnabled()) { log.info("using template [" + dQuery.getIndexName() + ";" + dTemplate.getName() + "]"); } dQE = TemplateRegistryController.applyTemplate(dQuery, dTemplate, this); } else { // transform dQE = getSimpleSearchQuery(dQuery); } final LuceneSearchResult searchResult = doQuery(dQE, (dQuery.getStartHits() != null ? dQuery.getStartHits().intValue() : 0)); // add result attributes if (dQuery.getResultFields() != null) { addResultAttributes(dQuery.getResultFields(), searchResult); } // add highlight attributes if (dQuery.getHighlightFields() != null) { addHighlightResultAttributes(dQuery.getHighlightFields(), searchResult, dQE); } return searchResult; } catch (final TemplateException e) { log.error("error while NQE transformation", e); throw new IndexException("unable to apply templates", e); } catch (final NodeTransformerException e) { log.error("unable to perform node transformation", e); throw new IndexException("unable to perform node transformation", e); } } /** * Adds the value of index field as Attributes to the LuceneSearchResult. * * @param resultDefinition * the result definition * @param searchResult * the LuceneSearchResult * @throws IndexException * if any error occurs */ private void addResultAttributes(final Collection<Integer> resultFields, final LuceneSearchResult searchResult) throws IndexException { for (final Record record : searchResult.getResultList()) { for (final int fieldNo : resultFields) { final DIndexField field = _index.getIndexStructure().getField(fieldNo); // TODO: map from fieldNo to Attribute name final String name = field.getName(); final Any attributeValue; final String[] values = getResultValues(record.getId(), fieldNo); if (values != null) { if (values.length == 1) { attributeValue = createValue(record.getFactory(), fieldNo, values[0]); } else { final AnySeq seq = record.getFactory().createAnySeq(); attributeValue = seq; for (final String value : values) { seq.add(createValue(record.getFactory(), fieldNo, value)); } } record.getMetadata().put(name, attributeValue); } } } } /** * Adds the highlight annotations for the configured highlighting results. * * @param highlightFields * the highlightFields * @param searchResult * the LuceneSearchResult * @param dQE * the IQueryExpression * @throws IndexException * if any errror occurs */ private void addHighlightResultAttributes(final Collection<Integer> highlightFields, final LuceneSearchResult searchResult, final IQueryExpression dQE) throws IndexException { for (final Record record : searchResult.getResultList()) { final AnyMap highlight = record.getFactory().createAnyMap(); record.getMetadata().put(SearchResultConstants.HIGHLIGHT, highlight); for (final int fieldNo : highlightFields) { final DIndexField field = _index.getIndexStructure().getField(fieldNo); // TODO: map from fieldNo to Attribute name final String name = field.getName(); addHighlightAnnotation(dQE, record.getId(), highlight, fieldNo, name, searchResult.getIndexName()); } // for } // for } /** * create value from string using the appropriate type method. * * @param fieldNo * the fieldNo * @param value * the string value * @throws IndexException * if any error occurs * @return value */ protected abstract Value createValue(DataFactory f, final int fieldNo, final String value) throws IndexException; /** * Adds a highlighting annotation on the given record and attribute * * @param dQE * the IQueryExpression * @param recordId * record id * @param highlight * the highlight annotation. * @param fieldNo * the fieldNo * @param attributeName * the name of the attribute * @param indexName * the name of the index * @throws IndexException * if any errorr occurs */ protected abstract void addHighlightAnnotation(final IQueryExpression dQE, final String recordId, final AnyMap highlight, int fieldNo, final String attributeName, final String indexName) throws IndexException; /** * Execute test query. * * @param text * Query text. * @return Result. * @throws IndexException * Unable to perform test query. */ public LuceneSearchResult doTestQuery(final String text) throws IndexException { return doQuery(getTestQueryExpression(text), 0); } /** * This method applies a given FieldConfig to a field. * * @param field * Search field. * @param nc * Named config. * @param defaultfc * Default config. */ private void applyFieldConfig(final DField field, final DFieldConfig nc, final DFieldConfig defaultfc) { final Log log = LogFactory.getLog(getClass()); StringBuffer sb = new StringBuffer("Processing parameters for search field [" + field.getFieldNo() + "]: "); if (nc == null) { sb = sb.append("[no named config] "); } if (field.getConstraint() == null) { if (nc == null || nc.getConstraint() == null) { field.setConstraint(defaultfc.getConstraint()); sb = sb.append("DC:"); } else { field.setConstraint(nc.getConstraint()); sb = sb.append("NC:"); } } sb = sb.append("Constraint=" + field.getConstraint() + " "); if (field.getFieldTemplate() == null) { if (nc == null || nc.getFieldTemplate() == null) { field.setFieldTemplate(defaultfc.getFieldTemplate()); sb = sb.append("DC:"); } else { field.setFieldTemplate(nc.getFieldTemplate()); sb = sb.append("NC:"); } } sb = sb.append("FieldTemplate=" + field.getFieldTemplate() + " "); if (field.getNodeTransformer() == null) { if (nc == null || nc.getNodeTransformer() == null) { field.setNodeTransformer(defaultfc.getNodeTransformer()); sb = sb.append("DC:"); } else { field.setNodeTransformer(nc.getNodeTransformer()); sb = sb.append("NC:"); } } sb = sb.append("NodeTransformer=" + (field.getNodeTransformer() == null ? "" : field.getNodeTransformer().getName()) + " "); if (field.getWeight() == null) { if (nc == null || nc.getWeight() == null) { field.setWeight(defaultfc.getWeight()); sb = sb.append("DC:"); } else { field.setWeight(nc.getWeight()); sb = sb.append("NC:"); } } sb = sb.append("Weight=" + field.getWeight() + " "); // include technologie dependant parameter assignation if (field instanceof org.eclipse.smila.search.utils.search.DTextField) { final org.eclipse.smila.search.utils.search.DTextField tf = (org.eclipse.smila.search.utils.search.DTextField) field; // assign named config parameters if (nc != null) { final ITFParameter tfp = ((org.eclipse.smila.search.datadictionary.messages.ddconfig.DTextField) nc) .getParameter(); insertTextParameter(tf, tfp, "NC", sb); } // assign default config parameters final ITFParameter tfp = ((org.eclipse.smila.search.datadictionary.messages.ddconfig.DTextField) defaultfc) .getParameter(); insertTextParameter(tf, tfp, "DC", sb); if (tf.getParameter() != null) { tf.setParameter((ITFParameter) tf.getParameter().clone()); } } else if (field instanceof org.eclipse.smila.search.utils.search.DNumberField) { final org.eclipse.smila.search.utils.search.DNumberField nf = (org.eclipse.smila.search.utils.search.DNumberField) field; // assign named config parameters if (nc != null) { final INFParameter nfp = ((org.eclipse.smila.search.datadictionary.messages.ddconfig.DNumberField) nc) .getParameter(); insertNumberParameter(nf, nfp, "NC", sb); } // assign default config parameters final INFParameter nfp = ((org.eclipse.smila.search.datadictionary.messages.ddconfig.DNumberField) defaultfc) .getParameter(); insertNumberParameter(nf, nfp, "DC", sb); if (nf.getParameter() != null) { nf.setParameter((INFParameter) nf.getParameter().clone()); } } else if (field instanceof org.eclipse.smila.search.utils.search.DDateField) { final org.eclipse.smila.search.utils.search.DDateField df = (org.eclipse.smila.search.utils.search.DDateField) field; // assign named config parameters if (nc != null) { final IDFParameter dfp = ((org.eclipse.smila.search.datadictionary.messages.ddconfig.DDateField) nc) .getParameter(); insertDateParameter(df, dfp, "NC", sb); } // assign default config parameters final IDFParameter dfp = ((org.eclipse.smila.search.datadictionary.messages.ddconfig.DDateField) defaultfc) .getParameter(); insertDateParameter(df, dfp, "DC", sb); if (df.getParameter() != null) { df.setParameter((IDFParameter) df.getParameter().clone()); } } if (log.isDebugEnabled()) { log.debug(sb.toString()); } } /** * Unlocks a locked index. * * @throws IndexException * if any error occurs */ public abstract void unlock() throws IndexException; }