Index: impl/java/org/apache/lenya/cms/publication/DocumentImpl.java
===================================================================
--- impl/java/org/apache/lenya/cms/publication/DocumentImpl.java (revision 654051)
+++ impl/java/org/apache/lenya/cms/publication/DocumentImpl.java (working copy)
@@ -23,6 +23,7 @@
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
+import java.util.Iterator;
import java.util.List;
import org.apache.avalon.framework.container.ContainerUtil;
@@ -39,12 +40,17 @@
import org.apache.lenya.cms.repository.ContentHolder;
import org.apache.lenya.cms.repository.Node;
import org.apache.lenya.cms.repository.NodeFactory;
+import org.apache.lenya.cms.repository.Persistable;
import org.apache.lenya.cms.repository.RepositoryException;
import org.apache.lenya.cms.repository.Session;
import org.apache.lenya.cms.site.Link;
import org.apache.lenya.cms.site.SiteException;
import org.apache.lenya.cms.site.SiteStructure;
import org.apache.lenya.util.Assert;
+import org.apache.lenya.xml.DocumentHelper;
+import org.apache.xpath.XPathAPI;
+import org.w3c.dom.Attr;
+import org.w3c.dom.traversal.NodeIterator;
/**
* A typical CMS document.
@@ -557,6 +563,17 @@
public Node getRepositoryNode() {
if (this.repositoryNode == null) {
this.repositoryNode = getRepositoryNode(this.manager, getFactory(), getSourceURI());
+ try {
+ Persistable persistable = this.repositoryNode.getPersistable();
+ if (!(persistable instanceof ComposedPersistable)) {
+ ComposedPersistable compPersistable = new ComposedPersistable();
+ compPersistable.add(new LinkExtractor(this));
+ compPersistable.add(persistable);
+ this.repositoryNode.setPersistable(compPersistable);
+ }
+ } catch (RepositoryException e) {
+ throw new RuntimeException(e);
+ }
}
return this.repositoryNode;
}
@@ -706,5 +723,105 @@
}
return this.revision;
}
+
+ protected static class ComposedPersistable implements Persistable {
+
+ private List persistables = new ArrayList();
+
+ public void add(Persistable persistable) {
+ Assert.notNull("persistable", persistable);
+ this.persistables.add(persistable);
+ }
+ public boolean isModified() {
+ for (Iterator i = this.persistables.iterator(); i.hasNext(); ) {
+ Persistable persistable = (Persistable) i.next();
+ if (persistable.isModified()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void save() throws RepositoryException {
+ for (Iterator i = this.persistables.iterator(); i.hasNext(); ) {
+ Persistable persistable = (Persistable) i.next();
+ persistable.save();
+ }
+ }
+
+ }
+
+ protected static class LinkExtractor implements Persistable {
+
+ protected static final String LINK_METADATA_NAMESPACE = "http://apache.org/lenya/metadata/link/1.0";
+ protected static final String KEY_OUTGOING_LINKS = "outgoingLinks";
+
+ private Document doc;
+
+ public LinkExtractor(Document doc) {
+ this.doc = doc;
+ }
+
+ public boolean isModified() {
+ return true;
+ }
+
+ public void save() throws RepositoryException {
+ try {
+ MetaData linkMetaData = this.doc.getMetaData(LINK_METADATA_NAMESPACE);
+ linkMetaData.removeAllValues(KEY_OUTGOING_LINKS);
+ String[] xPaths = this.doc.getResourceType().getLinkAttributeXPaths();
+ if (xPaths.length > 0) {
+ org.w3c.dom.Document xml = DocumentHelper.readDocument(this.doc.getInputStream());
+
+ if (xml == null) {
+ throw new RuntimeException("The document [" + this
+ + "] doesn't contain any XML content.");
+ }
+ for (int i = 0; i < xPaths.length; i++) {
+ NodeIterator iter = XPathAPI.selectNodeIterator(xml, xPaths[i]);
+ org.w3c.dom.Node node;
+ while ((node = iter.nextNode()) != null) {
+ Attr attr = (Attr) node;
+ String uri = attr.getValue();
+ if (isLinkUri(uri)) {
+ String linkUri = getDocumentLinkUri(uri);
+ linkMetaData.addValue(KEY_OUTGOING_LINKS, linkUri);
+ }
+ }
+ }
+ }
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * @param uri The URI as used in the content.
+ * @return The actual link URI without anchor and query string.
+ */
+ protected String getDocumentLinkUri(String uri) {
+ String docUri = uri;
+ docUri = removeSuffix(docUri, '#');
+ docUri = removeSuffix(docUri, '?');
+ return docUri;
+ }
+
+ protected String removeSuffix(String docUri, char delimiter) {
+ int anchorIndex = docUri.indexOf(delimiter);
+ if (anchorIndex > -1) {
+ docUri = docUri.substring(0, anchorIndex);
+ }
+ return docUri;
+ }
+
+ protected boolean isLinkUri(String uri) {
+ return uri.startsWith("lenya-document:");
+ }
+ }
+
+
}
\ No newline at end of file
Index: java/org/apache/lenya/cms/search/SearchUtil.java
===================================================================
--- java/org/apache/lenya/cms/search/SearchUtil.java (revision 0)
+++ java/org/apache/lenya/cms/search/SearchUtil.java (revision 0)
@@ -0,0 +1,38 @@
+/*
+ * 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.apache.lenya.cms.search;
+
+/**
+ * Search utility class.
+ */
+public class SearchUtil {
+
+ /**
+ * Returns the field name for a meta data element. Caution: The field name
+ * has to be escaped using
+ * {@link org.apache.lucene.queryParser.QueryParser#escape(String)} to use
+ * it in query strings.
+ * @param namespaceUri The namespace URI of the meta data element set.
+ * @param elementName The name of the meta data element.
+ * @return The field name.
+ */
+ public static final String getMetaDataFieldName(String namespaceUri, String elementName) {
+ return "{" + namespaceUri + "}" + elementName;
+ }
+
+}
Index: java/org/apache/lenya/cms/search/SearchException.java
===================================================================
--- java/org/apache/lenya/cms/search/SearchException.java (revision 0)
+++ java/org/apache/lenya/cms/search/SearchException.java (revision 0)
@@ -0,0 +1,23 @@
+package org.apache.lenya.cms.search;
+
+public class SearchException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ public SearchException() {
+ super();
+ }
+
+ public SearchException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public SearchException(String message) {
+ super(message);
+ }
+
+ public SearchException(Throwable cause) {
+ super(cause);
+ }
+
+}
Index: java/org/apache/lenya/cms/search/SearchResult.java
===================================================================
--- java/org/apache/lenya/cms/search/SearchResult.java (revision 0)
+++ java/org/apache/lenya/cms/search/SearchResult.java (revision 0)
@@ -0,0 +1,70 @@
+/*
+ * 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.apache.lenya.cms.search;
+
+/**
+ * A search result.
+ */
+public interface SearchResult {
+
+ /**
+ * Shortcut for getFieldValue(DocumentSearcher.FIELD_PUBLICATION).
+ * @return The publication ID or null
if the result doesn't
+ * contain this field.
+ */
+ String getPublicationId();
+
+ /**
+ * Shortcut for getFieldValue(DocumentSearcher.FIELD_UUID).
+ * @return The UUID or null
if the result doesn't contain
+ * this field.
+ */
+ String getUuid();
+
+ /**
+ * Shortcut for getFieldValue(DocumentSearcher.FIELD_LANGUAGE).
+ * @return The language or null
if the result doesn't contain
+ * this field.
+ */
+ String getLanguage();
+
+ /**
+ * @return The names of the fields of this result.
+ */
+ String[] getFieldNames();
+
+ /**
+ * @param fieldName The name of the field.
+ * @return the field value or null
if the result doesn't
+ * contain this field.
+ */
+ String getFieldValue(String fieldName);
+
+ /**
+ * @param fieldName The name of the field.
+ * @return an array of values or null
if the result doesn't
+ * contain this field.
+ */
+ String[] getFieldValues(String fieldName);
+
+ /**
+ * @return The score of this result.
+ */
+ float getScore();
+
+}
Index: java/org/apache/lenya/cms/search/SearchResults.java
===================================================================
--- java/org/apache/lenya/cms/search/SearchResults.java (revision 0)
+++ java/org/apache/lenya/cms/search/SearchResults.java (revision 0)
@@ -0,0 +1,36 @@
+/*
+ * 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.apache.lenya.cms.search;
+
+/**
+ * A list of search results.
+ */
+public interface SearchResults {
+
+ /**
+ * @return The number of search results in this list.
+ */
+ int getLength();
+
+ /**
+ * @param number The number of the result to return.
+ * @return A search result.
+ */
+ SearchResult getResult(int number);
+
+}
Index: java/org/apache/lenya/cms/search/DocumentSearcher.java
===================================================================
--- java/org/apache/lenya/cms/search/DocumentSearcher.java (revision 0)
+++ java/org/apache/lenya/cms/search/DocumentSearcher.java (revision 0)
@@ -0,0 +1,64 @@
+/*
+ * 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.apache.lenya.cms.search;
+
+import org.apache.lucene.search.Query;
+
+/**
+ * Service to search for documents.
+ */
+public interface DocumentSearcher {
+
+ /**
+ * The name of the field which stores the publication ID.
+ */
+ String FIELD_PUBLICATION = "publication";
+
+ /**
+ * The name of the field which stores the UUID.
+ */
+ String FIELD_UUID = "uuid";
+
+ /**
+ * The name of the field which stores the language.
+ */
+ String FIELD_LANGUAGE = "language";
+
+ /**
+ * The service role.
+ */
+ String ROLE = DocumentSearcher.class.getName();
+
+ /**
+ * @param indexId The ID of the index to use.
+ * @param queryString The query string, conforming to the Lucene query parser syntax.
+ * @param defaultField The field to search if no field is specified in the query string.
+ * @return A search results object.
+ * @throws SearchException if the index does not exist or the query string is invalid.
+ */
+ SearchResults find(String indexId, String queryString, String defaultField) throws SearchException;
+
+ /**
+ * @param indexId The ID of the index to use.
+ * @param query The query.
+ * @return A search results object.
+ * @throws SearchException if the index does not exist.
+ */
+ SearchResults find(String indexId, Query query) throws SearchException;
+
+}
Index: modules/lucene/java/src/org/apache/cocoon/components/search/components/impl/IndexManagerImpl.java
===================================================================
--- modules/lucene/java/src/org/apache/cocoon/components/search/components/impl/IndexManagerImpl.java (revision 654051)
+++ modules/lucene/java/src/org/apache/cocoon/components/search/components/impl/IndexManagerImpl.java (working copy)
@@ -50,9 +50,9 @@
import org.apache.lenya.cms.publication.DocumentFactory;
import org.apache.lenya.cms.publication.DocumentUtil;
import org.apache.lenya.cms.publication.Publication;
-import org.apache.lenya.cms.publication.PublicationException;
import org.apache.lenya.cms.publication.PublicationManager;
-import org.apache.lenya.modules.lucene.MetaDataFieldRegistry;
+import org.apache.lenya.cms.search.DocumentSearcher;
+import org.apache.lenya.cms.search.SearchUtil;
/**
* Index Manager Component. Configure and Manage the differents indexes.
@@ -290,7 +290,7 @@
*/
private void addIndexes(Configuration configuration) throws ConfigurationException {
AnalyzerManager analyzerManager = null;
- MetaDataFieldRegistry registry = null;
+ MetaDataRegistry registry = null;
Configuration[] confs = configuration.getChildren(INDEX_ELEMENT);
@@ -299,7 +299,7 @@
}
try {
analyzerManager = (AnalyzerManager) this.manager.lookup(AnalyzerManager.ROLE);
- registry = (MetaDataFieldRegistry) this.manager.lookup(MetaDataFieldRegistry.ROLE);
+ registry = (MetaDataRegistry) this.manager.lookup(MetaDataRegistry.ROLE);
// configure each index
for (int i = 0; i < confs.length; i++) {
@@ -318,14 +318,18 @@
addMetaDataFieldDefinitions(registry, docdecl);
- FieldDefinition uuidDef = FieldDefinition.create("uuid", FieldDefinition.KEYWORD);
+ FieldDefinition uuidDef = FieldDefinition.create(DocumentSearcher.FIELD_UUID, FieldDefinition.KEYWORD);
uuidDef.setStore(true);
docdecl.addFieldDef(uuidDef);
- FieldDefinition langDef = FieldDefinition.create("language", FieldDefinition.KEYWORD);
+ FieldDefinition langDef = FieldDefinition.create(DocumentSearcher.FIELD_LANGUAGE, FieldDefinition.KEYWORD);
langDef.setStore(true);
docdecl.addFieldDef(langDef);
+ FieldDefinition pubDef = FieldDefinition.create(DocumentSearcher.FIELD_PUBLICATION, FieldDefinition.KEYWORD);
+ pubDef.setStore(true);
+ docdecl.addFieldDef(pubDef);
+
for (int j = 0; j < fields.length; j++) {
FieldDefinition fielddecl;
@@ -398,13 +402,19 @@
}
}
- protected void addMetaDataFieldDefinitions(MetaDataFieldRegistry registry,
+ protected void addMetaDataFieldDefinitions(MetaDataRegistry registry,
IndexStructure indexStructure) throws MetaDataException {
- String[] fieldNames = registry.getFieldNames();
- for (int i = 0; i < fieldNames.length; i++) {
- FieldDefinition fieldDef = FieldDefinition.create(fieldNames[i], FieldDefinition.TEXT);
- fieldDef.setStore(false);
- indexStructure.addFieldDef(fieldDef);
+
+ String[] namespaces = registry.getNamespaceUris();
+ for (int n = 0; n < namespaces.length; n++) {
+ ElementSet elementSet = registry.getElementSet(namespaces[n]);
+ Element[] elements = elementSet.getElements();
+ for (int e = 0; e < elements.length; e++) {
+ String fieldName = SearchUtil.getMetaDataFieldName(namespaces[n], elements[e].getName());
+ FieldDefinition fieldDef = FieldDefinition.create(fieldName, FieldDefinition.TEXT);
+ fieldDef.setStore(false);
+ indexStructure.addFieldDef(fieldDef);
+ }
}
}
Index: modules/lucene/java/src/org/apache/cocoon/transformation/LuceneIndexTransformer2.java
===================================================================
--- modules/lucene/java/src/org/apache/cocoon/transformation/LuceneIndexTransformer2.java (revision 654051)
+++ modules/lucene/java/src/org/apache/cocoon/transformation/LuceneIndexTransformer2.java (working copy)
@@ -596,7 +596,7 @@
}
/**
- * Set a custum analyzer (default: the analyzer of the index).
+ * Set a custom analyzer (default: the analyzer of the index).
* @param analyzerId The analyzer ID (may be null)
* @throws SAXException if an error occurs.
*/
Index: modules/lucene/java/src/org/apache/lenya/modules/lucene/impl/LuceneSearchResult.java
===================================================================
--- modules/lucene/java/src/org/apache/lenya/modules/lucene/impl/LuceneSearchResult.java (revision 0)
+++ modules/lucene/java/src/org/apache/lenya/modules/lucene/impl/LuceneSearchResult.java (revision 0)
@@ -0,0 +1,71 @@
+package org.apache.lenya.modules.lucene.impl;
+
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.lenya.cms.search.SearchResult;
+import org.apache.lenya.util.Assert;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+
+public class LuceneSearchResult implements SearchResult {
+
+ private Document document;
+ private float score;
+
+ protected LuceneSearchResult(Document luceneDoc, float score) {
+ Assert.notNull("lucene document", luceneDoc);
+ this.document = luceneDoc;
+ this.score = score;
+ }
+
+ private Set fieldNames;
+
+ protected Set fieldNames() {
+ if (this.fieldNames == null) {
+ this.fieldNames = new HashSet();
+ for (Enumeration e = this.document.fields(); e.hasMoreElements();) {
+ Field field = (Field) e.nextElement();
+ this.fieldNames.add(field.name());
+ }
+ }
+ return this.fieldNames;
+ }
+
+ public String[] getFieldNames() {
+ Set names = fieldNames();
+ return (String[]) names.toArray(new String[names.size()]);
+ }
+
+ public float getScore() {
+ return this.score;
+ }
+
+ public String getFieldValue(String fieldName) {
+ confirmFieldExists(fieldName);
+ return this.document.get(fieldName);
+ }
+
+ public String[] getFieldValues(String fieldName) {
+ confirmFieldExists(fieldName);
+ return this.document.getValues(fieldName);
+ }
+
+ protected void confirmFieldExists(String fieldName) {
+ Assert.isTrue("field '" + fieldName + "' exists", fieldNames().contains(fieldName));
+ }
+
+ public String getLanguage() {
+ return getFieldValue(LuceneDocumentSearcher.FIELD_LANGUAGE);
+ }
+
+ public String getPublicationId() {
+ return getFieldValue(LuceneDocumentSearcher.FIELD_PUBLICATION);
+ }
+
+ public String getUuid() {
+ return getFieldValue(LuceneDocumentSearcher.FIELD_UUID);
+ }
+
+}
Index: modules/lucene/java/src/org/apache/lenya/modules/lucene/impl/LuceneSearchResults.java
===================================================================
--- modules/lucene/java/src/org/apache/lenya/modules/lucene/impl/LuceneSearchResults.java (revision 0)
+++ modules/lucene/java/src/org/apache/lenya/modules/lucene/impl/LuceneSearchResults.java (revision 0)
@@ -0,0 +1,42 @@
+package org.apache.lenya.modules.lucene.impl;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.lenya.cms.publication.DocumentFactory;
+import org.apache.lenya.cms.search.SearchResult;
+import org.apache.lenya.cms.search.SearchResults;
+import org.apache.lenya.util.Assert;
+import org.apache.lucene.search.Hits;
+
+public class LuceneSearchResults implements SearchResults {
+
+ private Hits hits;
+ private Map number2result = new HashMap();
+
+ public LuceneSearchResults(Hits hits) {
+ Assert.notNull("hits", hits);
+ this.hits = hits;
+ }
+
+ public int getLength() {
+ return this.hits.length();
+ }
+
+ public SearchResult getResult(int number) {
+ Integer key = new Integer(number);
+ SearchResult result = (SearchResult) this.number2result.get(key);
+ if (result == null) {
+ try {
+ result = new LuceneSearchResult(this.hits.doc(number),
+ this.hits.score(number));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ this.number2result.put(key, result);
+ }
+ return result;
+ }
+
+}
Index: modules/lucene/java/src/org/apache/lenya/modules/lucene/impl/LuceneDocumentSearcher.java
===================================================================
--- modules/lucene/java/src/org/apache/lenya/modules/lucene/impl/LuceneDocumentSearcher.java (revision 0)
+++ modules/lucene/java/src/org/apache/lenya/modules/lucene/impl/LuceneDocumentSearcher.java (revision 0)
@@ -0,0 +1,95 @@
+/*
+ * 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.apache.lenya.modules.lucene.impl;
+
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+import org.apache.avalon.framework.thread.ThreadSafe;
+import org.apache.cocoon.ProcessingException;
+import org.apache.cocoon.components.search.Index;
+import org.apache.cocoon.components.search.LuceneCocoonSearcher;
+import org.apache.cocoon.components.search.components.AnalyzerManager;
+import org.apache.cocoon.components.search.components.IndexManager;
+import org.apache.lenya.cms.search.DocumentSearcher;
+import org.apache.lenya.cms.search.SearchResults;
+import org.apache.lucene.search.Hits;
+import org.apache.lucene.search.Query;
+
+public class LuceneDocumentSearcher extends AbstractLogEnabled implements DocumentSearcher,
+ Serviceable, ThreadSafe {
+
+ private ServiceManager manager;
+
+ public SearchResults find(String indexId, final String queryString, final String defaultField) {
+ return getResults(indexId, new ResultExtractor() {
+ public SearchResults getResults(LuceneCocoonSearcher searcher)
+ throws ProcessingException {
+ Hits hits = searcher.search(queryString, defaultField);
+ return new LuceneSearchResults(hits);
+ }
+ });
+ }
+
+ public void service(ServiceManager manager) throws ServiceException {
+ this.manager = manager;
+ }
+
+ public SearchResults find(String indexId, final Query query) {
+ return getResults(indexId, new ResultExtractor() {
+ public SearchResults getResults(LuceneCocoonSearcher searcher)
+ throws ProcessingException {
+ Hits hits = searcher.search(query);
+ return new LuceneSearchResults(hits);
+ }
+ });
+ }
+
+ protected interface ResultExtractor {
+ SearchResults getResults(LuceneCocoonSearcher searcher) throws ProcessingException;
+ }
+
+ protected SearchResults getResults(String indexId, ResultExtractor extractor) {
+ IndexManager indexManager = null;
+ AnalyzerManager analyzerManager = null;
+ LuceneCocoonSearcher searcher = null;
+ try {
+ searcher = (LuceneCocoonSearcher) this.manager.lookup(LuceneCocoonSearcher.ROLE);
+ indexManager = (IndexManager) this.manager.lookup(IndexManager.ROLE);
+ analyzerManager = (AnalyzerManager) this.manager.lookup(AnalyzerManager.ROLE);
+ Index index = indexManager.getIndex(indexId);
+ String analyzerId = "standard";
+ searcher.setAnalyzer(analyzerManager.getAnalyzer(analyzerId));
+ searcher.setDirectory(index.getDirectory());
+ return extractor.getResults(searcher);
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ } finally {
+ if (indexManager != null) {
+ this.manager.release(indexManager);
+ }
+ if (analyzerManager != null) {
+ this.manager.release(analyzerManager);
+ }
+ }
+ }
+
+}
Index: modules/lucene/java/src/org/apache/lenya/modules/lucene/QueryStringModule.java
===================================================================
--- modules/lucene/java/src/org/apache/lenya/modules/lucene/QueryStringModule.java (revision 654051)
+++ modules/lucene/java/src/org/apache/lenya/modules/lucene/QueryStringModule.java (working copy)
@@ -31,7 +31,9 @@
import org.apache.lenya.cms.metadata.Element;
import org.apache.lenya.cms.metadata.ElementSet;
import org.apache.lenya.cms.metadata.MetaDataRegistry;
+import org.apache.lenya.cms.search.SearchUtil;
import org.apache.lucene.index.Term;
+import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.TermQuery;
@@ -44,20 +46,15 @@
protected static final String PARAM_QUERY_STRING = "queryString";
protected static final String[] DEFAULT_FIELDS = { "body" };
- protected static final char[] ESCAPED_CHARACTERS = { '+', '-', '&', '|', '!', '(', ')', '{',
- '}', '[', ']', '^', '"', '~', '*', '?', ':', '\\' };
-
protected ServiceManager manager;
public Object getAttribute(String name, Configuration modeConf, Map objectModel)
throws ConfigurationException {
-
if (name.equals("queryString")) {
return getQueryString(objectModel);
} else {
throw new IllegalArgumentException("The attribute [" + name + "] is not supported.");
}
-
}
protected String getQueryString(Map objectModel) {
@@ -68,6 +65,10 @@
return "";
}
+ if (searchTerm.indexOf(':') > -1) {
+ return searchTerm;
+ }
+
if (searchTerm.indexOf(' ') > -1) {
searchTerm = "(" + searchTerm + ")";
}
@@ -89,17 +90,15 @@
}
MetaDataRegistry registry = null;
- MetaDataFieldRegistry fieldRegistry = null;
try {
registry = (MetaDataRegistry) this.manager.lookup(MetaDataRegistry.ROLE);
- fieldRegistry = (MetaDataFieldRegistry) this.manager.lookup(MetaDataFieldRegistry.ROLE);
String[] namespaces = registry.getNamespaceUris();
for (int n = 0; n < namespaces.length; n++) {
ElementSet elementSet = registry.getElementSet(namespaces[n]);
Element[] elements = elementSet.getElements();
for (int e = 0; e < elements.length; e++) {
if (elements[e].isSearchable()) {
- String field = fieldRegistry.getFieldName(namespaces[n], elements[e].getName());
+ String field = SearchUtil.getMetaDataFieldName(namespaces[n], elements[e].getName());
TermQuery termQuery = new TermQuery(getTerm(field, searchTerm));
query.add(termQuery, false, false);
}
@@ -111,38 +110,12 @@
if (registry != null) {
this.manager.release(registry);
}
- if (fieldRegistry != null) {
- this.manager.release(fieldRegistry);
- }
}
return query;
}
- protected boolean shallEscape(char c) {
- for (int i = 0; i < ESCAPED_CHARACTERS.length; i++) {
- if (ESCAPED_CHARACTERS[i] == c) {
- return true;
- }
- }
- return false;
- }
-
- protected String escape(final String prefix) {
- StringBuffer buffer = new StringBuffer();
- StringCharacterIterator i = new StringCharacterIterator(prefix);
- char c = i.current();
- while (c != CharacterIterator.DONE) {
- if (shallEscape(c)) {
- buffer.append('\\');
- }
- buffer.append(c);
- c = i.next();
- }
- return buffer.toString();
- }
-
protected Term getTerm(String field, String value) {
- return new Term(escape(field), value);
+ return new Term(QueryParser.escape(field), value);
}
public void service(ServiceManager manager) throws ServiceException {
Index: modules/lucene/config/advancedSearch.xml
===================================================================
--- modules/lucene/config/advancedSearch.xml (revision 0)
+++ modules/lucene/config/advancedSearch.xml (revision 0)
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
Index: modules/lucene/config/cocoon-xconf/documentsearcher.xconf
===================================================================
--- modules/lucene/config/cocoon-xconf/documentsearcher.xconf (revision 0)
+++ modules/lucene/config/cocoon-xconf/documentsearcher.xconf (revision 0)
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
Index: modules/lucene/sitemap.xmap
===================================================================
--- modules/lucene/sitemap.xmap (revision 654051)
+++ modules/lucene/sitemap.xmap (working copy)
@@ -145,6 +145,7 @@
+
Index: modules/lucene/xslt/metadata2index.xsl
===================================================================
--- modules/lucene/xslt/metadata2index.xsl (revision 654051)
+++ modules/lucene/xslt/metadata2index.xsl (working copy)
@@ -13,8 +13,26 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Index: modules/lucene/xslt/index.xsl
===================================================================
--- modules/lucene/xslt/index.xsl (revision 654051)
+++ modules/lucene/xslt/index.xsl (working copy)
@@ -8,12 +8,14 @@
>
+
+
Index: modules-core/linking/java/src/org/apache/lenya/cms/linking/impl/MetaDataLinkManager.java
===================================================================
--- modules-core/linking/java/src/org/apache/lenya/cms/linking/impl/MetaDataLinkManager.java (revision 0)
+++ modules-core/linking/java/src/org/apache/lenya/cms/linking/impl/MetaDataLinkManager.java (revision 0)
@@ -0,0 +1,167 @@
+/*
+ * 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.apache.lenya.cms.linking.impl;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.avalon.framework.activity.Disposable;
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+import org.apache.lenya.cms.linking.Link;
+import org.apache.lenya.cms.linking.LinkManager;
+import org.apache.lenya.cms.linking.LinkResolver;
+import org.apache.lenya.cms.linking.LinkTarget;
+import org.apache.lenya.cms.metadata.MetaData;
+import org.apache.lenya.cms.metadata.MetaDataException;
+import org.apache.lenya.cms.publication.Area;
+import org.apache.lenya.cms.publication.Document;
+import org.apache.lenya.cms.publication.DocumentFactory;
+import org.apache.lenya.cms.publication.Publication;
+import org.apache.lenya.cms.publication.PublicationException;
+import org.apache.lenya.cms.search.DocumentSearcher;
+import org.apache.lenya.cms.search.SearchResult;
+import org.apache.lenya.cms.search.SearchResults;
+import org.apache.lenya.cms.search.SearchUtil;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.queryParser.QueryParser;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.WildcardQuery;
+
+public class MetaDataLinkManager extends AbstractLogEnabled implements LinkManager, Serviceable,
+ Disposable {
+
+ protected static final String LINK_NAMESPACE = "http://apache.org/lenya/metadata/link/1.0";
+ protected static final String ELEMENT_OUTGOING_LINKS = "outgoingLinks";
+
+ private ServiceManager manager;
+ private DocumentSearcher searcher;
+ private LinkResolver linkResolver;
+
+ public Link[] getLinksFrom(Document source) {
+ try {
+ String[] uris = getOutgoingLinkUris(source);
+ Link[] links = new Link[uris.length];
+ for (int i = 0; i < links.length; i++) {
+ links[i] = new Link(uris[i]);
+ }
+ return links;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected String[] getOutgoingLinkUris(Document source) throws MetaDataException {
+ MetaData meta = source.getMetaData(LINK_NAMESPACE);
+ return meta.getValues(ELEMENT_OUTGOING_LINKS);
+ }
+
+ public Document[] getReferencingDocuments(Document target) {
+ try {
+ DocumentSearcher searcher = getSearcher();
+ String indexId = target.getPublication().getId() + "-" + target.getArea();
+ //String queryString = getReferencingDocumentsQueryString(searcher, target);
+ //SearchResults results = searcher.find(indexId, queryString, "");
+
+ String fieldName = SearchUtil.getMetaDataFieldName(LINK_NAMESPACE, ELEMENT_OUTGOING_LINKS);
+ String value = QueryParser.escape("lenya-document:" + target.getUUID() + "*");
+ SearchResults results = searcher.find(indexId, value, fieldName);
+
+ Set matchingDocs = new HashSet();
+ LinkResolver resolver = getLinkResolver();
+ for (int d = 0; d < results.getLength(); d++) {
+ SearchResult result = results.getResult(d);
+ DocumentFactory factory = target.getFactory();
+ String areaName = target.getArea();
+ Document doc = getDocument(result, factory, areaName);
+ if (doc != null) {
+ String[] links = getOutgoingLinkUris(doc);
+ for (int l = 0; l < links.length; l++) {
+ LinkTarget linkTarget = resolver.resolve(doc, links[l]);
+ if (linkTarget.exists() && linkTarget.getDocument() == target) {
+ matchingDocs.add(doc);
+ }
+ }
+ }
+ }
+ return (Document[]) matchingDocs.toArray(new Document[matchingDocs.size()]);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected Document getDocument(SearchResult result, DocumentFactory factory, String areaName)
+ throws PublicationException {
+ Document doc = null;
+ String uuid = result.getUuid();
+ String language = result.getLanguage();
+ String pubId = result.getPublicationId();
+ if (uuid != null && language != null && pubId != null) {
+ Publication pub = factory.getPublication(pubId);
+ Area area = pub.getArea(areaName);
+ if (area.contains(uuid, language)) {
+ doc = area.getDocument(uuid, language);
+ }
+ }
+ return doc;
+ }
+
+ protected String getReferencingDocumentsQueryString(DocumentSearcher searcher, Document target) {
+ String fieldName = SearchUtil.getMetaDataFieldName(LINK_NAMESPACE, ELEMENT_OUTGOING_LINKS);
+ String value = "lenya-document:" + target.getUUID() + "*";
+ return QueryParser.escape(fieldName) + ":" + QueryParser.escape(value);
+ }
+
+ protected DocumentSearcher getSearcher() {
+ if (this.searcher == null) {
+ try {
+ this.searcher = (DocumentSearcher) this.manager.lookup(DocumentSearcher.ROLE);
+ } catch (ServiceException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return this.searcher;
+ }
+
+ protected LinkResolver getLinkResolver() {
+ if (this.linkResolver == null) {
+ try {
+ this.linkResolver = (LinkResolver) this.manager.lookup(LinkResolver.ROLE);
+ } catch (ServiceException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return this.linkResolver;
+ }
+
+ public void service(ServiceManager manager) throws ServiceException {
+ this.manager = manager;
+ }
+
+ public void dispose() {
+ if (this.searcher != null) {
+ this.manager.release(searcher);
+ }
+ if (this.linkResolver == null) {
+ this.manager.release(this.linkResolver);
+ }
+ }
+
+}
Index: modules-core/linking/config/cocoon-xconf/linkmanager.xconf
===================================================================
--- modules-core/linking/config/cocoon-xconf/linkmanager.xconf (revision 654051)
+++ modules-core/linking/config/cocoon-xconf/linkmanager.xconf (working copy)
@@ -21,6 +21,6 @@
+ class="org.apache.lenya.cms.linking.impl.MetaDataLinkManager"/>
Index: modules-core/linking/config/cocoon-xconf/link-metadata.xconf
===================================================================
--- modules-core/linking/config/cocoon-xconf/link-metadata.xconf (revision 0)
+++ modules-core/linking/config/cocoon-xconf/link-metadata.xconf (revision 0)
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+