mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
Moving to root below branch label
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2005 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl;
|
||||
|
||||
import org.alfresco.repo.search.AbstractSearcherComponent;
|
||||
import org.alfresco.service.cmr.repository.Path;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.cmr.search.QueryParameter;
|
||||
import org.alfresco.service.cmr.search.QueryParameterDefinition;
|
||||
import org.alfresco.service.cmr.search.ResultSet;
|
||||
import org.alfresco.service.cmr.search.SearchParameters;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
/**
|
||||
* Simple searcher against another store using the JSR 170 API.
|
||||
* <p>
|
||||
* This class is not fully implemented and hence still abstract.
|
||||
*/
|
||||
public abstract class JCR170Searcher extends AbstractSearcherComponent
|
||||
{
|
||||
public ResultSet query(StoreRef store, String language, String query, Path[] queryOptions,
|
||||
QueryParameter[] queryParameters)
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public ResultSet query(StoreRef store, String language, String query, Path[] attributePaths, QueryParameterDefinition[] queryParameterDefinitions)
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public ResultSet query(StoreRef store, QName queryId, QueryParameter[] queryParameters)
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public ResultSet query(SearchParameters searchParameters)
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl;
|
||||
|
||||
import org.alfresco.repo.search.Indexer;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
|
||||
/**
|
||||
* A no action indexer - the indexing is done automatically along with
|
||||
* persistence
|
||||
*
|
||||
* TODO: Rename to Adaptor?
|
||||
*
|
||||
* @author andyh
|
||||
*
|
||||
*/
|
||||
public class NoActionIndexer implements Indexer
|
||||
{
|
||||
|
||||
public void createNode(ChildAssociationRef relationshipRef)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public void updateNode(NodeRef nodeRef)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public void deleteNode(ChildAssociationRef relationshipRef)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public void createChildRelationship(ChildAssociationRef relationshipRef)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public void updateChildRelationship(ChildAssociationRef relationshipBeforeRef, ChildAssociationRef relationshipAfterRef)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public void deleteChildRelationship(ChildAssociationRef relationshipRef)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
279
source/java/org/alfresco/repo/search/impl/NodeSearcher.java
Normal file
279
source/java/org/alfresco/repo/search/impl/NodeSearcher.java
Normal file
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import org.alfresco.repo.search.DocumentNavigator;
|
||||
import org.alfresco.repo.search.NodeServiceXPath;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.XPathException;
|
||||
import org.alfresco.service.cmr.search.QueryParameterDefinition;
|
||||
import org.alfresco.service.cmr.search.SearchService;
|
||||
import org.alfresco.service.namespace.NamespacePrefixResolver;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.jaxen.JaxenException;
|
||||
|
||||
/**
|
||||
* Helper class that walks a node hierarchy.
|
||||
* <p>
|
||||
* Some searcher methods on
|
||||
* {@link org.alfresco.service.cmr.search.SearchService} can use this directly
|
||||
* as its only dependencies are
|
||||
* {@link org.alfresco.service.cmr.repository.NodeService},
|
||||
* {@link org.alfresco.service.cmr.dictionary.DictionaryService} and a
|
||||
* {@link org.alfresco.service.cmr.search.SearchService}
|
||||
*
|
||||
* @author Derek Hulley
|
||||
*/
|
||||
public class NodeSearcher
|
||||
{
|
||||
private NodeService nodeService;
|
||||
|
||||
private DictionaryService dictionaryService;
|
||||
|
||||
private SearchService searchService;
|
||||
|
||||
public NodeSearcher(NodeService nodeService, DictionaryService dictionaryService, SearchService searchService)
|
||||
{
|
||||
this.nodeService = nodeService;
|
||||
this.dictionaryService = dictionaryService;
|
||||
this.searchService = searchService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see NodeServiceXPath
|
||||
*/
|
||||
public synchronized List<NodeRef> selectNodes(NodeRef contextNodeRef, String xpathIn,
|
||||
QueryParameterDefinition[] paramDefs, NamespacePrefixResolver namespacePrefixResolver,
|
||||
boolean followAllParentLinks, String language)
|
||||
{
|
||||
try
|
||||
{
|
||||
String xpath = xpathIn;
|
||||
boolean useJCRXPath = language.equalsIgnoreCase(SearchService.LANGUAGE_JCR_XPATH);
|
||||
|
||||
List<AttributeOrder> order = null;
|
||||
|
||||
// replace element
|
||||
if (useJCRXPath)
|
||||
{
|
||||
order = new ArrayList<AttributeOrder>();
|
||||
// We do not allow variable substitution with this pattern
|
||||
xpath = xpath.replaceAll("element\\(\\s*(\\*|\\w*:\\w*)\\s*,\\s*(\\*|\\w*:\\w*)\\s*\\)",
|
||||
"$1[subtypeOf(\"$2\")]");
|
||||
String split[] = xpath.split("order\\s*by\\s*", 2);
|
||||
xpath = split[0];
|
||||
|
||||
if (split.length > 1 && split[1].length() > 0)
|
||||
{
|
||||
String clauses[] = split[1].split("\\s,\\s");
|
||||
|
||||
for (String clause : clauses)
|
||||
{
|
||||
if (clause.startsWith("@"))
|
||||
{
|
||||
String attribute = clause.replaceFirst("@(\\p{Alpha}[\\w:]*)(?:\\s+(.*))?", "$1");
|
||||
String sort = clause.replaceFirst("@(\\p{Alpha}[\\w:]*)(?:\\s+(.*))?", "$2");
|
||||
|
||||
if (sort.length() == 0)
|
||||
{
|
||||
sort = "ascending";
|
||||
}
|
||||
|
||||
QName attributeQName = QName.createQName(attribute, namespacePrefixResolver);
|
||||
order.add(new AttributeOrder(attributeQName, sort.equalsIgnoreCase("ascending")));
|
||||
}
|
||||
else if (clause.startsWith("jcr:score"))
|
||||
{
|
||||
// ignore jcr:score ordering
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException("Malformed order by expression " + split[1]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DocumentNavigator documentNavigator = new DocumentNavigator(dictionaryService, nodeService, searchService,
|
||||
namespacePrefixResolver, followAllParentLinks, useJCRXPath);
|
||||
NodeServiceXPath nsXPath = new NodeServiceXPath(xpath, documentNavigator, paramDefs);
|
||||
for (String prefix : namespacePrefixResolver.getPrefixes())
|
||||
{
|
||||
nsXPath.addNamespace(prefix, namespacePrefixResolver.getNamespaceURI(prefix));
|
||||
}
|
||||
List list = nsXPath.selectNodes(nodeService.getPrimaryParent(contextNodeRef));
|
||||
HashSet<NodeRef> unique = new HashSet<NodeRef>(list.size());
|
||||
for (Object o : list)
|
||||
{
|
||||
if (o instanceof ChildAssociationRef)
|
||||
{
|
||||
unique.add(((ChildAssociationRef) o).getChildRef());
|
||||
}
|
||||
else if (o instanceof DocumentNavigator.Property)
|
||||
{
|
||||
unique.add(((DocumentNavigator.Property) o).parent);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new XPathException("Xpath expression must only select nodes");
|
||||
}
|
||||
}
|
||||
|
||||
List<NodeRef> answer = new ArrayList<NodeRef>(unique.size());
|
||||
answer.addAll(unique);
|
||||
if (order != null)
|
||||
{
|
||||
orderNodes(answer, order);
|
||||
for(NodeRef node : answer)
|
||||
{
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
for (AttributeOrder attOrd : order)
|
||||
{
|
||||
buffer.append(" ").append(nodeService.getProperty(node, attOrd.attribute));
|
||||
}
|
||||
System.out.println(buffer.toString());
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
catch (JaxenException e)
|
||||
{
|
||||
throw new XPathException("Error executing xpath: \n" + " xpath: " + xpathIn, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void orderNodes(List<NodeRef> answer, List<AttributeOrder> order)
|
||||
{
|
||||
Collections.sort(answer, new NodeRefComparator(nodeService, order));
|
||||
}
|
||||
|
||||
static class NodeRefComparator implements Comparator<NodeRef>
|
||||
{
|
||||
List<AttributeOrder> order;
|
||||
NodeService nodeService;
|
||||
|
||||
NodeRefComparator(NodeService nodeService, List<AttributeOrder> order)
|
||||
{
|
||||
this.nodeService = nodeService;
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public int compare(NodeRef n1, NodeRef n2)
|
||||
{
|
||||
for (AttributeOrder attributeOrder : order)
|
||||
{
|
||||
Serializable o1 = nodeService.getProperty(n1, attributeOrder.attribute);
|
||||
Serializable o2 = nodeService.getProperty(n2, attributeOrder.attribute);
|
||||
|
||||
if (o1 == null)
|
||||
{
|
||||
if (o2 == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return attributeOrder.ascending ? -1 : 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (o2 == null)
|
||||
{
|
||||
return attributeOrder.ascending ? 1 : -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((o1 instanceof Comparable) && (o2 instanceof Comparable))
|
||||
{
|
||||
return (attributeOrder.ascending ? 1 : -1) * ((Comparable)o1).compareTo((Comparable) o2);
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see NodeServiceXPath
|
||||
*/
|
||||
public List<Serializable> selectProperties(NodeRef contextNodeRef, String xpath,
|
||||
QueryParameterDefinition[] paramDefs, NamespacePrefixResolver namespacePrefixResolver,
|
||||
boolean followAllParentLinks, String language)
|
||||
{
|
||||
try
|
||||
{
|
||||
boolean useJCRXPath = language.equalsIgnoreCase(SearchService.LANGUAGE_JCR_XPATH);
|
||||
|
||||
DocumentNavigator documentNavigator = new DocumentNavigator(dictionaryService, nodeService, searchService,
|
||||
namespacePrefixResolver, followAllParentLinks, useJCRXPath);
|
||||
NodeServiceXPath nsXPath = new NodeServiceXPath(xpath, documentNavigator, paramDefs);
|
||||
for (String prefix : namespacePrefixResolver.getPrefixes())
|
||||
{
|
||||
nsXPath.addNamespace(prefix, namespacePrefixResolver.getNamespaceURI(prefix));
|
||||
}
|
||||
List list = nsXPath.selectNodes(nodeService.getPrimaryParent(contextNodeRef));
|
||||
List<Serializable> answer = new ArrayList<Serializable>(list.size());
|
||||
for (Object o : list)
|
||||
{
|
||||
if (!(o instanceof DocumentNavigator.Property))
|
||||
{
|
||||
throw new XPathException("Xpath expression must only select nodes");
|
||||
}
|
||||
answer.add(((DocumentNavigator.Property) o).value);
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
catch (JaxenException e)
|
||||
{
|
||||
throw new XPathException("Error executing xpath", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static class AttributeOrder
|
||||
{
|
||||
QName attribute;
|
||||
|
||||
boolean ascending;
|
||||
|
||||
AttributeOrder(QName attribute, boolean ascending)
|
||||
{
|
||||
this.attribute = attribute;
|
||||
this.ascending = ascending;
|
||||
}
|
||||
}
|
||||
}
|
110
source/java/org/alfresco/repo/search/impl/lucene/CharStream.java
Normal file
110
source/java/org/alfresco/repo/search/impl/lucene/CharStream.java
Normal file
@@ -0,0 +1,110 @@
|
||||
/* Generated By:JavaCC: Do not edit this line. CharStream.java Version 3.0 */
|
||||
package org.alfresco.repo.search.impl.lucene;
|
||||
|
||||
/**
|
||||
* This interface describes a character stream that maintains line and
|
||||
* column number positions of the characters. It also has the capability
|
||||
* to backup the stream to some extent. An implementation of this
|
||||
* interface is used in the TokenManager implementation generated by
|
||||
* JavaCCParser.
|
||||
*
|
||||
* All the methods except backup can be implemented in any fashion. backup
|
||||
* needs to be implemented correctly for the correct operation of the lexer.
|
||||
* Rest of the methods are all used to get information like line number,
|
||||
* column number and the String that constitutes a token and are not used
|
||||
* by the lexer. Hence their implementation won't affect the generated lexer's
|
||||
* operation.
|
||||
*/
|
||||
|
||||
public interface CharStream {
|
||||
|
||||
/**
|
||||
* Returns the next character from the selected input. The method
|
||||
* of selecting the input is the responsibility of the class
|
||||
* implementing this interface. Can throw any java.io.IOException.
|
||||
*/
|
||||
char readChar() throws java.io.IOException;
|
||||
|
||||
/**
|
||||
* Returns the column position of the character last read.
|
||||
* @deprecated
|
||||
* @see #getEndColumn
|
||||
*/
|
||||
int getColumn();
|
||||
|
||||
/**
|
||||
* Returns the line number of the character last read.
|
||||
* @deprecated
|
||||
* @see #getEndLine
|
||||
*/
|
||||
int getLine();
|
||||
|
||||
/**
|
||||
* Returns the column number of the last character for current token (being
|
||||
* matched after the last call to BeginTOken).
|
||||
*/
|
||||
int getEndColumn();
|
||||
|
||||
/**
|
||||
* Returns the line number of the last character for current token (being
|
||||
* matched after the last call to BeginTOken).
|
||||
*/
|
||||
int getEndLine();
|
||||
|
||||
/**
|
||||
* Returns the column number of the first character for current token (being
|
||||
* matched after the last call to BeginTOken).
|
||||
*/
|
||||
int getBeginColumn();
|
||||
|
||||
/**
|
||||
* Returns the line number of the first character for current token (being
|
||||
* matched after the last call to BeginTOken).
|
||||
*/
|
||||
int getBeginLine();
|
||||
|
||||
/**
|
||||
* Backs up the input stream by amount steps. Lexer calls this method if it
|
||||
* had already read some characters, but could not use them to match a
|
||||
* (longer) token. So, they will be used again as the prefix of the next
|
||||
* token and it is the implemetation's responsibility to do this right.
|
||||
*/
|
||||
void backup(int amount);
|
||||
|
||||
/**
|
||||
* Returns the next character that marks the beginning of the next token.
|
||||
* All characters must remain in the buffer between two successive calls
|
||||
* to this method to implement backup correctly.
|
||||
*/
|
||||
char BeginToken() throws java.io.IOException;
|
||||
|
||||
/**
|
||||
* Returns a string made up of characters from the marked token beginning
|
||||
* to the current buffer position. Implementations have the choice of returning
|
||||
* anything that they want to. For example, for efficiency, one might decide
|
||||
* to just return null, which is a valid implementation.
|
||||
*/
|
||||
String GetImage();
|
||||
|
||||
/**
|
||||
* Returns an array of characters that make up the suffix of length 'len' for
|
||||
* the currently matched token. This is used to build up the matched string
|
||||
* for use in actions in the case of MORE. A simple and inefficient
|
||||
* implementation of this is as follows :
|
||||
*
|
||||
* {
|
||||
* String t = GetImage();
|
||||
* return t.substring(t.length() - len, t.length()).toCharArray();
|
||||
* }
|
||||
*/
|
||||
char[] GetSuffix(int len);
|
||||
|
||||
/**
|
||||
* The lexer calls this function to indicate that it is done with the stream
|
||||
* and hence implementations can free any resources held by this class.
|
||||
* Again, the body of this function can be just empty and it will not
|
||||
* affect the lexer's operation.
|
||||
*/
|
||||
void Done();
|
||||
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
import org.apache.lucene.store.Directory;
|
||||
|
||||
public class ClosingIndexSearcher extends IndexSearcher
|
||||
{
|
||||
IndexReader reader;
|
||||
|
||||
public ClosingIndexSearcher(String path) throws IOException
|
||||
{
|
||||
super(path);
|
||||
}
|
||||
|
||||
public ClosingIndexSearcher(Directory directory) throws IOException
|
||||
{
|
||||
super(directory);
|
||||
}
|
||||
|
||||
public ClosingIndexSearcher(IndexReader r)
|
||||
{
|
||||
super(r);
|
||||
this.reader = r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException
|
||||
{
|
||||
super.close();
|
||||
if(reader != null)
|
||||
{
|
||||
reader.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene;
|
||||
|
||||
import org.saxpath.Axis;
|
||||
import org.saxpath.SAXPathException;
|
||||
import org.saxpath.XPathHandler;
|
||||
|
||||
import com.werken.saxpath.XPathReader;
|
||||
|
||||
public class DebugXPathHandler implements XPathHandler
|
||||
{
|
||||
|
||||
public DebugXPathHandler()
|
||||
{
|
||||
super();
|
||||
// TODO Auto-generated constructor stub
|
||||
}
|
||||
|
||||
public void endAbsoluteLocationPath() throws SAXPathException
|
||||
{
|
||||
System.out.println("End Absolute Location Path");
|
||||
}
|
||||
|
||||
public void endAdditiveExpr(int arg0) throws SAXPathException
|
||||
{
|
||||
System.out.println("End Additive Expr: value = " + arg0);
|
||||
}
|
||||
|
||||
public void endAllNodeStep() throws SAXPathException
|
||||
{
|
||||
System.out.println("End All Node Step");
|
||||
}
|
||||
|
||||
public void endAndExpr(boolean arg0) throws SAXPathException
|
||||
{
|
||||
System.out.println("End And Expr: value = " + arg0);
|
||||
}
|
||||
|
||||
public void endCommentNodeStep() throws SAXPathException
|
||||
{
|
||||
System.out.println("End Comment Node Step");
|
||||
}
|
||||
|
||||
public void endEqualityExpr(int arg0) throws SAXPathException
|
||||
{
|
||||
System.out.println("End Equality Expr: value = " + arg0);
|
||||
}
|
||||
|
||||
public void endFilterExpr() throws SAXPathException
|
||||
{
|
||||
System.out.println("End Filter Expr");
|
||||
}
|
||||
|
||||
public void endFunction() throws SAXPathException
|
||||
{
|
||||
System.out.println("End Function");
|
||||
}
|
||||
|
||||
public void endMultiplicativeExpr(int arg0) throws SAXPathException
|
||||
{
|
||||
System.out.println("End Multiplicative Expr: value = " + arg0);
|
||||
}
|
||||
|
||||
public void endNameStep() throws SAXPathException
|
||||
{
|
||||
System.out.println("End Name Step");
|
||||
}
|
||||
|
||||
public void endOrExpr(boolean arg0) throws SAXPathException
|
||||
{
|
||||
System.out.println("End Or Expr: value = " + arg0);
|
||||
}
|
||||
|
||||
public void endPathExpr() throws SAXPathException
|
||||
{
|
||||
System.out.println("End Path Expression");
|
||||
}
|
||||
|
||||
public void endPredicate() throws SAXPathException
|
||||
{
|
||||
System.out.println("End Predicate");
|
||||
}
|
||||
|
||||
public void endProcessingInstructionNodeStep() throws SAXPathException
|
||||
{
|
||||
System.out.println("End Processing Instruction Node Step");
|
||||
}
|
||||
|
||||
public void endRelationalExpr(int arg0) throws SAXPathException
|
||||
{
|
||||
System.out.println("End Relational Expr: value = " + arg0);
|
||||
}
|
||||
|
||||
public void endRelativeLocationPath() throws SAXPathException
|
||||
{
|
||||
System.out.println("End Relative Location Path");
|
||||
}
|
||||
|
||||
public void endTextNodeStep() throws SAXPathException
|
||||
{
|
||||
System.out.println("End Text Node Step");
|
||||
}
|
||||
|
||||
public void endUnaryExpr(int arg0) throws SAXPathException
|
||||
{
|
||||
System.out.println("End Unary Expr: value = " + arg0);
|
||||
}
|
||||
|
||||
public void endUnionExpr(boolean arg0) throws SAXPathException
|
||||
{
|
||||
System.out.println("End Union Expr: value = " + arg0);
|
||||
}
|
||||
|
||||
public void endXPath() throws SAXPathException
|
||||
{
|
||||
System.out.println("End XPath");
|
||||
}
|
||||
|
||||
public void literal(String arg0) throws SAXPathException
|
||||
{
|
||||
System.out.println("Literal = " + arg0);
|
||||
}
|
||||
|
||||
public void number(double arg0) throws SAXPathException
|
||||
{
|
||||
System.out.println("Double = " + arg0);
|
||||
}
|
||||
|
||||
public void number(int arg0) throws SAXPathException
|
||||
{
|
||||
System.out.println("Integer = " + arg0);
|
||||
}
|
||||
|
||||
public void startAbsoluteLocationPath() throws SAXPathException
|
||||
{
|
||||
System.out.println("Start Absolute Location Path");
|
||||
}
|
||||
|
||||
public void startAdditiveExpr() throws SAXPathException
|
||||
{
|
||||
System.out.println("Start Additive Expression");
|
||||
}
|
||||
|
||||
public void startAllNodeStep(int arg0) throws SAXPathException
|
||||
{
|
||||
System.out.println("Start All Node Exp: Axis = " + Axis.lookup(arg0));
|
||||
}
|
||||
|
||||
public void startAndExpr() throws SAXPathException
|
||||
{
|
||||
System.out.println("Start AndExpression");
|
||||
}
|
||||
|
||||
public void startCommentNodeStep(int arg0) throws SAXPathException
|
||||
{
|
||||
System.out.println("Start Comment Node Step");
|
||||
}
|
||||
|
||||
public void startEqualityExpr() throws SAXPathException
|
||||
{
|
||||
System.out.println("Start Equality Expression");
|
||||
}
|
||||
|
||||
public void startFilterExpr() throws SAXPathException
|
||||
{
|
||||
System.out.println("Start Filter Expression");
|
||||
}
|
||||
|
||||
public void startFunction(String arg0, String arg1) throws SAXPathException
|
||||
{
|
||||
System.out.println("Start Function arg0 = < " + arg0 + " > arg1 = < " + arg1 + " >");
|
||||
}
|
||||
|
||||
public void startMultiplicativeExpr() throws SAXPathException
|
||||
{
|
||||
System.out.println("Start Multiplicative Expression");
|
||||
}
|
||||
|
||||
public void startNameStep(int arg0, String arg1, String arg2) throws SAXPathException
|
||||
{
|
||||
System.out.println("Start Name Step Axis = <" + Axis.lookup(arg0) + " > arg1 = < " + arg1 + " > arg 2 <" + arg2
|
||||
+ " >");
|
||||
}
|
||||
|
||||
public void startOrExpr() throws SAXPathException
|
||||
{
|
||||
System.out.println("Start Or Expression");
|
||||
}
|
||||
|
||||
public void startPathExpr() throws SAXPathException
|
||||
{
|
||||
System.out.println("Start Path Expression");
|
||||
}
|
||||
|
||||
public void startPredicate() throws SAXPathException
|
||||
{
|
||||
System.out.println("Start Predicate");
|
||||
}
|
||||
|
||||
public void startProcessingInstructionNodeStep(int arg0, String arg1) throws SAXPathException
|
||||
{
|
||||
System.out.println("Start Processing INstruction Node Step = < " + arg0 + " > arg1 = < " + arg1 + " >");
|
||||
}
|
||||
|
||||
public void startRelationalExpr() throws SAXPathException
|
||||
{
|
||||
System.out.println("Start Relationship Expression");
|
||||
}
|
||||
|
||||
public void startRelativeLocationPath() throws SAXPathException
|
||||
{
|
||||
System.out.println("Start Relative Location Path");
|
||||
}
|
||||
|
||||
public void startTextNodeStep(int arg0) throws SAXPathException
|
||||
{
|
||||
System.out.println("Start Text Node Step: value = " + arg0);
|
||||
}
|
||||
|
||||
public void startUnaryExpr() throws SAXPathException
|
||||
{
|
||||
System.out.println("Start Unary Expression");
|
||||
}
|
||||
|
||||
public void startUnionExpr() throws SAXPathException
|
||||
{
|
||||
System.out.println("Start Union Expression");
|
||||
}
|
||||
|
||||
public void startXPath() throws SAXPathException
|
||||
{
|
||||
System.out.println("Start XPath");
|
||||
}
|
||||
|
||||
public void variableReference(String arg0, String arg1) throws SAXPathException
|
||||
{
|
||||
System.out.println("Variable Reference arg0 = < " + arg0 + " > arg1 = < " + arg1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param args
|
||||
* @throws SAXPathException
|
||||
*/
|
||||
public static void main(String[] args) throws SAXPathException
|
||||
{
|
||||
XPathReader reader = new XPathReader();
|
||||
reader.setXPathHandler(new DebugXPathHandler());
|
||||
reader
|
||||
.parse("/ns:one[@woof='dog']/two/./../two[functionTest(@a, @b, $woof:woof)]/three/*/four//*/five/six[@exists1 and @exists2]");
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,122 @@
|
||||
// FastCharStream.java
|
||||
package org.alfresco.repo.search.impl.lucene;
|
||||
|
||||
/**
|
||||
* Copyright 2004 The Apache Software Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
|
||||
/** An efficient implementation of JavaCC's CharStream interface. <p>Note that
|
||||
* this does not do line-number counting, but instead keeps track of the
|
||||
* character position of the token in the input, as required by Lucene's {@link
|
||||
* org.apache.lucene.analysis.Token} API. */
|
||||
public final class FastCharStream implements CharStream {
|
||||
char[] buffer = null;
|
||||
|
||||
int bufferLength = 0; // end of valid chars
|
||||
int bufferPosition = 0; // next char to read
|
||||
|
||||
int tokenStart = 0; // offset in buffer
|
||||
int bufferStart = 0; // position in file of buffer
|
||||
|
||||
Reader input; // source of chars
|
||||
|
||||
/** Constructs from a Reader. */
|
||||
public FastCharStream(Reader r) {
|
||||
input = r;
|
||||
}
|
||||
|
||||
public final char readChar() throws IOException {
|
||||
if (bufferPosition >= bufferLength)
|
||||
refill();
|
||||
return buffer[bufferPosition++];
|
||||
}
|
||||
|
||||
private final void refill() throws IOException {
|
||||
int newPosition = bufferLength - tokenStart;
|
||||
|
||||
if (tokenStart == 0) { // token won't fit in buffer
|
||||
if (buffer == null) { // first time: alloc buffer
|
||||
buffer = new char[2048];
|
||||
} else if (bufferLength == buffer.length) { // grow buffer
|
||||
char[] newBuffer = new char[buffer.length*2];
|
||||
System.arraycopy(buffer, 0, newBuffer, 0, bufferLength);
|
||||
buffer = newBuffer;
|
||||
}
|
||||
} else { // shift token to front
|
||||
System.arraycopy(buffer, tokenStart, buffer, 0, newPosition);
|
||||
}
|
||||
|
||||
bufferLength = newPosition; // update state
|
||||
bufferPosition = newPosition;
|
||||
bufferStart += tokenStart;
|
||||
tokenStart = 0;
|
||||
|
||||
int charsRead = // fill space in buffer
|
||||
input.read(buffer, newPosition, buffer.length-newPosition);
|
||||
if (charsRead == -1)
|
||||
throw new IOException("read past eof");
|
||||
else
|
||||
bufferLength += charsRead;
|
||||
}
|
||||
|
||||
public final char BeginToken() throws IOException {
|
||||
tokenStart = bufferPosition;
|
||||
return readChar();
|
||||
}
|
||||
|
||||
public final void backup(int amount) {
|
||||
bufferPosition -= amount;
|
||||
}
|
||||
|
||||
public final String GetImage() {
|
||||
return new String(buffer, tokenStart, bufferPosition - tokenStart);
|
||||
}
|
||||
|
||||
public final char[] GetSuffix(int len) {
|
||||
char[] value = new char[len];
|
||||
System.arraycopy(buffer, bufferPosition - len, value, 0, len);
|
||||
return value;
|
||||
}
|
||||
|
||||
public final void Done() {
|
||||
try {
|
||||
input.close();
|
||||
} catch (IOException e) {
|
||||
System.err.println("Caught: " + e + "; ignoring.");
|
||||
}
|
||||
}
|
||||
|
||||
public final int getColumn() {
|
||||
return bufferStart + bufferPosition;
|
||||
}
|
||||
public final int getLine() {
|
||||
return 1;
|
||||
}
|
||||
public final int getEndColumn() {
|
||||
return bufferStart + bufferPosition;
|
||||
}
|
||||
public final int getEndLine() {
|
||||
return 1;
|
||||
}
|
||||
public final int getBeginColumn() {
|
||||
return bufferStart + tokenStart;
|
||||
}
|
||||
public final int getBeginLine() {
|
||||
return 1;
|
||||
}
|
||||
}
|
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.BitSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.apache.lucene.index.FilterIndexReader;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.index.TermDocs;
|
||||
import org.apache.lucene.index.TermEnum;
|
||||
import org.apache.lucene.index.TermPositions;
|
||||
|
||||
public class FilterIndexReaderByNodeRefs extends FilterIndexReader
|
||||
{
|
||||
BitSet deletedDocuments;
|
||||
|
||||
public FilterIndexReaderByNodeRefs(IndexReader reader, Set<NodeRef> deletions)
|
||||
{
|
||||
super(reader);
|
||||
deletedDocuments = new BitSet(reader.maxDoc());
|
||||
|
||||
try
|
||||
{
|
||||
for (NodeRef nodeRef : deletions)
|
||||
{
|
||||
TermDocs td = reader.termDocs(new Term("ID", nodeRef.toString()));
|
||||
while (td.next())
|
||||
{
|
||||
deletedDocuments.set(td.doc(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Failed to construct filtering index reader", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static class FilterTermDocs implements TermDocs
|
||||
{
|
||||
BitSet deletedDocuments;
|
||||
|
||||
protected TermDocs in;
|
||||
|
||||
public FilterTermDocs(TermDocs in, BitSet deletedDocuments)
|
||||
{
|
||||
this.in = in;
|
||||
this.deletedDocuments = deletedDocuments;
|
||||
}
|
||||
|
||||
public void seek(Term term) throws IOException
|
||||
{
|
||||
// Seek is left to the base implementation
|
||||
in.seek(term);
|
||||
}
|
||||
|
||||
public void seek(TermEnum termEnum) throws IOException
|
||||
{
|
||||
// Seek is left to the base implementation
|
||||
in.seek(termEnum);
|
||||
}
|
||||
|
||||
public int doc()
|
||||
{
|
||||
// The current document info is valid in the base implementation
|
||||
return in.doc();
|
||||
}
|
||||
|
||||
public int freq()
|
||||
{
|
||||
// The frequency is valid in the base implementation
|
||||
return in.freq();
|
||||
}
|
||||
|
||||
public boolean next() throws IOException
|
||||
{
|
||||
while(in.next())
|
||||
{
|
||||
if(!deletedDocuments.get(in.doc()))
|
||||
{
|
||||
// Not masked
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int read(int[] docs, int[] freqs) throws IOException
|
||||
{
|
||||
int[] innerDocs = new int[docs.length];
|
||||
int[] innerFreq = new int[docs.length];
|
||||
int count = in.read(innerDocs, innerFreq);
|
||||
|
||||
// Is the stream exhausted
|
||||
if (count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(allDeleted(innerDocs, count))
|
||||
{
|
||||
// Did not find anything - try again
|
||||
return read(docs, freqs);
|
||||
}
|
||||
|
||||
// Add non deleted
|
||||
|
||||
int insertPosition = 0;
|
||||
for(int i = 0; i < count; i++)
|
||||
{
|
||||
if(!deletedDocuments.get(innerDocs[i]))
|
||||
{
|
||||
docs[insertPosition] = innerDocs[i];
|
||||
freqs[insertPosition] = innerFreq[i];
|
||||
insertPosition++;
|
||||
}
|
||||
}
|
||||
|
||||
return insertPosition;
|
||||
}
|
||||
|
||||
private boolean allDeleted(int[] docs, int fillSize)
|
||||
{
|
||||
for(int i = 0; i < fillSize; i++)
|
||||
{
|
||||
if(!deletedDocuments.get(docs[i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean skipTo(int i) throws IOException
|
||||
{
|
||||
boolean result = in.skipTo(i);
|
||||
if(result == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(deletedDocuments.get(in.doc()))
|
||||
{
|
||||
return skipTo(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void close() throws IOException
|
||||
{
|
||||
// Leave to internal implementation
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
/** Base class for filtering {@link TermPositions} implementations. */
|
||||
public static class FilterTermPositions extends FilterTermDocs implements TermPositions
|
||||
{
|
||||
|
||||
public FilterTermPositions(TermPositions in, BitSet deletedDocuements)
|
||||
{
|
||||
super(in, deletedDocuements);
|
||||
}
|
||||
|
||||
public int nextPosition() throws IOException
|
||||
{
|
||||
return ((TermPositions) this.in).nextPosition();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int numDocs()
|
||||
{
|
||||
return super.numDocs() - deletedDocuments.cardinality();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TermDocs termDocs() throws IOException
|
||||
{
|
||||
return new FilterTermDocs(super.termDocs(), deletedDocuments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TermPositions termPositions() throws IOException
|
||||
{
|
||||
return new FilterTermPositions(super.termPositions(), deletedDocuments);
|
||||
}
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene;
|
||||
|
||||
import org.alfresco.repo.search.transaction.LuceneIndexLock;
|
||||
|
||||
public interface Lockable
|
||||
{
|
||||
public void setLuceneIndexLock(LuceneIndexLock luceneIndexLock);
|
||||
|
||||
public LuceneIndexLock getLuceneIndexLock();
|
||||
|
||||
public void getReadLock();
|
||||
|
||||
public void releaseReadLock();
|
||||
|
||||
public void getWriteLock();
|
||||
|
||||
public void releaseWriteLock();
|
||||
}
|
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.repo.search.impl.lucene.analysis.PathAnalyser;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.analysis.TokenStream;
|
||||
import org.apache.lucene.analysis.WhitespaceAnalyzer;
|
||||
import org.apache.lucene.analysis.standard.StandardAnalyzer;
|
||||
|
||||
/**
|
||||
* Analyse properties according to the property definition.
|
||||
*
|
||||
* The default is to use the standard tokeniser. The tokeniser should not have
|
||||
* been called when indexeing properties that require no tokenisation. (tokenise
|
||||
* should be set to false when adding the field to the document)
|
||||
*
|
||||
* @author andyh
|
||||
*
|
||||
*/
|
||||
|
||||
public class LuceneAnalyser extends Analyzer
|
||||
{
|
||||
|
||||
private DictionaryService dictionaryService;
|
||||
|
||||
private Analyzer defaultAnalyser;
|
||||
|
||||
private Map<String, Analyzer> analysers = new HashMap<String, Analyzer>();
|
||||
|
||||
/**
|
||||
* Constructs with a default standard analyser
|
||||
*
|
||||
* @param defaultAnalyzer
|
||||
* Any fields not specifically defined to use a different
|
||||
* analyzer will use the one provided here.
|
||||
*/
|
||||
public LuceneAnalyser(DictionaryService dictionaryService)
|
||||
{
|
||||
this(new StandardAnalyzer());
|
||||
this.dictionaryService = dictionaryService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs with default analyzer.
|
||||
*
|
||||
* @param defaultAnalyzer
|
||||
* Any fields not specifically defined to use a different
|
||||
* analyzer will use the one provided here.
|
||||
*/
|
||||
public LuceneAnalyser(Analyzer defaultAnalyser)
|
||||
{
|
||||
this.defaultAnalyser = defaultAnalyser;
|
||||
}
|
||||
|
||||
public TokenStream tokenStream(String fieldName, Reader reader)
|
||||
{
|
||||
Analyzer analyser = (Analyzer) analysers.get(fieldName);
|
||||
if (analyser == null)
|
||||
{
|
||||
analyser = findAnalyser(fieldName);
|
||||
}
|
||||
return analyser.tokenStream(fieldName, reader);
|
||||
}
|
||||
|
||||
private Analyzer findAnalyser(String fieldName)
|
||||
{
|
||||
Analyzer analyser;
|
||||
if (fieldName.equals("PATH"))
|
||||
{
|
||||
analyser = new PathAnalyser();
|
||||
}
|
||||
else if (fieldName.equals("QNAME"))
|
||||
{
|
||||
analyser = new PathAnalyser();
|
||||
}
|
||||
else if (fieldName.equals("TYPE"))
|
||||
{
|
||||
throw new UnsupportedOperationException("TYPE must not be tokenised");
|
||||
}
|
||||
else if (fieldName.equals("ASPECT"))
|
||||
{
|
||||
throw new UnsupportedOperationException("ASPECT must not be tokenised");
|
||||
}
|
||||
else if (fieldName.equals("ANCESTOR"))
|
||||
{
|
||||
analyser = new WhitespaceAnalyzer();
|
||||
}
|
||||
else if (fieldName.startsWith("@"))
|
||||
{
|
||||
QName propertyQName = QName.createQName(fieldName.substring(1));
|
||||
PropertyDefinition propertyDef = dictionaryService.getProperty(propertyQName);
|
||||
DataTypeDefinition dataType = (propertyDef == null) ? dictionaryService.getDataType(DataTypeDefinition.TEXT) : propertyDef.getDataType();
|
||||
String analyserClassName = dataType.getAnalyserClassName();
|
||||
try
|
||||
{
|
||||
Class<?> clazz = Class.forName(analyserClassName);
|
||||
analyser = (Analyzer)clazz.newInstance();
|
||||
}
|
||||
catch (ClassNotFoundException e)
|
||||
{
|
||||
throw new RuntimeException("Unable to load analyser for property " + fieldName.substring(1) + " of type " + dataType.getName() + " using " + analyserClassName);
|
||||
}
|
||||
catch (InstantiationException e)
|
||||
{
|
||||
throw new RuntimeException("Unable to load analyser for property " + fieldName.substring(1) + " of type " + dataType.getName() + " using " + analyserClassName);
|
||||
}
|
||||
catch (IllegalAccessException e)
|
||||
{
|
||||
throw new RuntimeException("Unable to load analyser for property " + fieldName.substring(1) + " of type " + dataType.getName() + " using " + analyserClassName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
analyser = defaultAnalyser;
|
||||
}
|
||||
analysers.put(fieldName, analyser);
|
||||
return analyser;
|
||||
}
|
||||
}
|
1019
source/java/org/alfresco/repo/search/impl/lucene/LuceneBase.java
Normal file
1019
source/java/org/alfresco/repo/search/impl/lucene/LuceneBase.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,311 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.search.ISO9075;
|
||||
import org.alfresco.repo.search.IndexerException;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.Path;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.cmr.search.CategoryService;
|
||||
import org.alfresco.service.cmr.search.ResultSet;
|
||||
import org.alfresco.service.cmr.search.ResultSetRow;
|
||||
import org.alfresco.service.namespace.NamespacePrefixResolver;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.bouncycastle.crypto.paddings.ISO7816d4Padding;
|
||||
|
||||
public class LuceneCategoryServiceImpl implements CategoryService
|
||||
{
|
||||
private NodeService nodeService;
|
||||
|
||||
private NamespacePrefixResolver namespacePrefixResolver;
|
||||
|
||||
private DictionaryService dictionaryService;
|
||||
|
||||
private LuceneIndexerAndSearcher indexerAndSearcher;
|
||||
|
||||
public LuceneCategoryServiceImpl()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
// Inversion of control support
|
||||
|
||||
public void setNodeService(NodeService nodeService)
|
||||
{
|
||||
this.nodeService = nodeService;
|
||||
}
|
||||
|
||||
public void setNamespacePrefixResolver(NamespacePrefixResolver namespacePrefixResolver)
|
||||
{
|
||||
this.namespacePrefixResolver = namespacePrefixResolver;
|
||||
}
|
||||
|
||||
public void setDictionaryService(DictionaryService dictionaryService)
|
||||
{
|
||||
this.dictionaryService = dictionaryService;
|
||||
}
|
||||
|
||||
public void setIndexerAndSearcher(LuceneIndexerAndSearcher indexerAndSearcher)
|
||||
{
|
||||
this.indexerAndSearcher = indexerAndSearcher;
|
||||
}
|
||||
|
||||
public Collection<ChildAssociationRef> getChildren(NodeRef categoryRef, Mode mode, Depth depth)
|
||||
{
|
||||
if (categoryRef == null)
|
||||
{
|
||||
return Collections.<ChildAssociationRef> emptyList();
|
||||
}
|
||||
ResultSet resultSet = null;
|
||||
try
|
||||
{
|
||||
StringBuilder luceneQuery = new StringBuilder(64);
|
||||
|
||||
if (!mode.equals(Mode.ALL))
|
||||
{
|
||||
luceneQuery.append(mode.equals(Mode.SUB_CATEGORIES) ? "-" : "").append("PATH_WITH_REPEATS:\"");
|
||||
luceneQuery.append(buildXPath(nodeService.getPath(categoryRef))).append("/");
|
||||
if (depth.equals(Depth.ANY))
|
||||
{
|
||||
luceneQuery.append("/");
|
||||
}
|
||||
luceneQuery.append("member").append("\" ");
|
||||
}
|
||||
|
||||
if (!mode.equals(Mode.MEMBERS))
|
||||
{
|
||||
luceneQuery.append("PATH_WITH_REPEATS:\"");
|
||||
luceneQuery.append(buildXPath(nodeService.getPath(categoryRef))).append("/");
|
||||
if (depth.equals(Depth.ANY))
|
||||
{
|
||||
luceneQuery.append("/");
|
||||
}
|
||||
luceneQuery.append("*").append("\" ");
|
||||
}
|
||||
|
||||
resultSet = indexerAndSearcher.getSearcher(categoryRef.getStoreRef(), false).query(categoryRef.getStoreRef(), "lucene", luceneQuery.toString(), null, null);
|
||||
|
||||
return resultSetToChildAssocCollection(resultSet);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (resultSet != null)
|
||||
{
|
||||
resultSet.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String buildXPath(Path path)
|
||||
{
|
||||
StringBuilder pathBuffer = new StringBuilder(64);
|
||||
for (Iterator<Path.Element> elit = path.iterator(); elit.hasNext(); /**/)
|
||||
{
|
||||
Path.Element element = elit.next();
|
||||
if (!(element instanceof Path.ChildAssocElement))
|
||||
{
|
||||
throw new IndexerException("Confused path: " + path);
|
||||
}
|
||||
Path.ChildAssocElement cae = (Path.ChildAssocElement) element;
|
||||
if (cae.getRef().getParentRef() != null)
|
||||
{
|
||||
pathBuffer.append("/");
|
||||
pathBuffer.append(getPrefix(cae.getRef().getQName().getNamespaceURI()));
|
||||
pathBuffer.append(ISO9075.encode(cae.getRef().getQName().getLocalName()));
|
||||
}
|
||||
}
|
||||
return pathBuffer.toString();
|
||||
}
|
||||
|
||||
HashMap<String, String> prefixLookup = new HashMap<String, String>();
|
||||
|
||||
private String getPrefix(String uri)
|
||||
{
|
||||
String prefix = prefixLookup.get(uri);
|
||||
if (prefix == null)
|
||||
{
|
||||
Collection<String> prefixes = namespacePrefixResolver.getPrefixes(uri);
|
||||
for (String first : prefixes)
|
||||
{
|
||||
prefix = first;
|
||||
break;
|
||||
}
|
||||
|
||||
prefixLookup.put(uri, prefix);
|
||||
}
|
||||
if (prefix == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
else
|
||||
{
|
||||
return prefix + ":";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Collection<ChildAssociationRef> resultSetToChildAssocCollection(ResultSet resultSet)
|
||||
{
|
||||
List<ChildAssociationRef> collection = new ArrayList<ChildAssociationRef>();
|
||||
if (resultSet != null)
|
||||
{
|
||||
for (ResultSetRow row : resultSet)
|
||||
{
|
||||
ChildAssociationRef car = nodeService.getPrimaryParent(row.getNodeRef());
|
||||
collection.add(car);
|
||||
}
|
||||
}
|
||||
return collection;
|
||||
// The caller closes the result set
|
||||
}
|
||||
|
||||
public Collection<ChildAssociationRef> getCategories(StoreRef storeRef, QName aspectQName, Depth depth)
|
||||
{
|
||||
Collection<ChildAssociationRef> assocs = new ArrayList<ChildAssociationRef>();
|
||||
Set<NodeRef> nodeRefs = getClassificationNodes(storeRef, aspectQName);
|
||||
for (NodeRef nodeRef : nodeRefs)
|
||||
{
|
||||
assocs.addAll(getChildren(nodeRef, Mode.SUB_CATEGORIES, depth));
|
||||
}
|
||||
return assocs;
|
||||
}
|
||||
|
||||
private Set<NodeRef> getClassificationNodes(StoreRef storeRef, QName qname)
|
||||
{
|
||||
ResultSet resultSet = null;
|
||||
try
|
||||
{
|
||||
resultSet = indexerAndSearcher.getSearcher(storeRef, false).query(storeRef, "lucene", "PATH_WITH_REPEATS:\"/" + getPrefix(qname.getNamespaceURI()) + ISO9075.encode(qname.getLocalName()) + "\"",
|
||||
null, null);
|
||||
|
||||
Set<NodeRef> nodeRefs = new HashSet<NodeRef>(resultSet.length());
|
||||
for (ResultSetRow row : resultSet)
|
||||
{
|
||||
nodeRefs.add(row.getNodeRef());
|
||||
}
|
||||
|
||||
return nodeRefs;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (resultSet != null)
|
||||
{
|
||||
resultSet.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<ChildAssociationRef> getClassifications(StoreRef storeRef)
|
||||
{
|
||||
ResultSet resultSet = null;
|
||||
try
|
||||
{
|
||||
resultSet = indexerAndSearcher.getSearcher(storeRef, false).query(storeRef, "lucene", "PATH_WITH_REPEATS:\"//cm:categoryRoot/*\"", null, null);
|
||||
return resultSetToChildAssocCollection(resultSet);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (resultSet != null)
|
||||
{
|
||||
resultSet.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<QName> getClassificationAspects()
|
||||
{
|
||||
List<QName> list = new ArrayList<QName>();
|
||||
for (QName aspect : dictionaryService.getAllAspects())
|
||||
{
|
||||
if (dictionaryService.isSubClass(aspect, ContentModel.ASPECT_CLASSIFIABLE))
|
||||
{
|
||||
list.add(aspect);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public NodeRef createClassifiction(StoreRef storeRef, QName typeName, String attributeName)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public Collection<ChildAssociationRef> getRootCategories(StoreRef storeRef, QName aspectName)
|
||||
{
|
||||
Collection<ChildAssociationRef> assocs = new ArrayList<ChildAssociationRef>();
|
||||
Set<NodeRef> nodeRefs = getClassificationNodes(storeRef, aspectName);
|
||||
for (NodeRef nodeRef : nodeRefs)
|
||||
{
|
||||
assocs.addAll(getChildren(nodeRef, Mode.SUB_CATEGORIES, Depth.IMMEDIATE));
|
||||
}
|
||||
return assocs;
|
||||
}
|
||||
|
||||
public NodeRef createCategory(NodeRef parent, String name)
|
||||
{
|
||||
if(!nodeService.exists(parent))
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Missing category?");
|
||||
}
|
||||
String uri = nodeService.getPrimaryParent(parent).getQName().getNamespaceURI();
|
||||
String validLocalName = QName.createValidLocalName(name);
|
||||
NodeRef newCategory = nodeService.createNode(parent, ContentModel.ASSOC_SUBCATEGORIES, QName.createQName(uri, validLocalName), ContentModel.TYPE_CATEGORY).getChildRef();
|
||||
nodeService.setProperty(newCategory, ContentModel.PROP_NAME, name);
|
||||
return newCategory;
|
||||
}
|
||||
|
||||
public NodeRef createRootCategory(StoreRef storeRef, QName aspectName, String name)
|
||||
{
|
||||
Set<NodeRef> nodeRefs = getClassificationNodes(storeRef, aspectName);
|
||||
if(nodeRefs.size() == 0)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Missing classification: "+aspectName);
|
||||
}
|
||||
NodeRef parent = nodeRefs.iterator().next();
|
||||
return createCategory(parent, name);
|
||||
}
|
||||
|
||||
public void deleteCategory(NodeRef nodeRef)
|
||||
{
|
||||
nodeService.deleteNode(nodeRef);
|
||||
}
|
||||
|
||||
public void deleteClassification(StoreRef storeRef, QName aspectName)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,672 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Random;
|
||||
|
||||
import javax.transaction.UserTransaction;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.dictionary.DictionaryDAO;
|
||||
import org.alfresco.repo.dictionary.M2Aspect;
|
||||
import org.alfresco.repo.dictionary.M2Model;
|
||||
import org.alfresco.repo.dictionary.M2Property;
|
||||
import org.alfresco.repo.search.impl.lucene.fts.FullTextSearchIndexer;
|
||||
import org.alfresco.repo.search.transaction.LuceneIndexLock;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.cmr.search.CategoryService;
|
||||
import org.alfresco.service.cmr.search.ResultSet;
|
||||
import org.alfresco.service.cmr.search.ResultSetRow;
|
||||
import org.alfresco.service.cmr.search.SearchService;
|
||||
import org.alfresco.service.namespace.DynamicNamespacePrefixResolver;
|
||||
import org.alfresco.service.namespace.NamespacePrefixResolver;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.service.transaction.TransactionService;
|
||||
import org.alfresco.util.ApplicationContextHelper;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
public class LuceneCategoryTest extends TestCase
|
||||
{
|
||||
private ServiceRegistry serviceRegistry;
|
||||
|
||||
static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
|
||||
NodeService nodeService;
|
||||
DictionaryService dictionaryService;
|
||||
LuceneIndexLock luceneIndexLock;
|
||||
private NodeRef rootNodeRef;
|
||||
private NodeRef n1;
|
||||
private NodeRef n2;
|
||||
private NodeRef n3;
|
||||
private NodeRef n4;
|
||||
private NodeRef n6;
|
||||
private NodeRef n5;
|
||||
private NodeRef n7;
|
||||
private NodeRef n8;
|
||||
private NodeRef n9;
|
||||
private NodeRef n10;
|
||||
private NodeRef n11;
|
||||
private NodeRef n12;
|
||||
private NodeRef n13;
|
||||
private NodeRef n14;
|
||||
|
||||
private NodeRef catContainer;
|
||||
private NodeRef catRoot;
|
||||
private NodeRef catACBase;
|
||||
private NodeRef catACOne;
|
||||
private NodeRef catACTwo;
|
||||
private NodeRef catACThree;
|
||||
private FullTextSearchIndexer luceneFTS;
|
||||
private DictionaryDAO dictionaryDAO;
|
||||
private String TEST_NAMESPACE = "http://www.alfresco.org/test/lucenecategorytest";
|
||||
private QName regionCategorisationQName;
|
||||
private QName assetClassCategorisationQName;
|
||||
private QName investmentRegionCategorisationQName;
|
||||
private QName marketingRegionCategorisationQName;
|
||||
private NodeRef catRBase;
|
||||
private NodeRef catROne;
|
||||
private NodeRef catRTwo;
|
||||
private NodeRef catRThree;
|
||||
private SearchService searcher;
|
||||
private LuceneIndexerAndSearcher indexerAndSearcher;
|
||||
|
||||
private CategoryService categoryService;
|
||||
|
||||
public LuceneCategoryTest()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
public LuceneCategoryTest(String arg0)
|
||||
{
|
||||
super(arg0);
|
||||
}
|
||||
|
||||
public void setUp() throws Exception
|
||||
{
|
||||
nodeService = (NodeService)ctx.getBean("dbNodeService");
|
||||
luceneIndexLock = (LuceneIndexLock)ctx.getBean("luceneIndexLock");
|
||||
dictionaryService = (DictionaryService)ctx.getBean("dictionaryService");
|
||||
luceneFTS = (FullTextSearchIndexer) ctx.getBean("LuceneFullTextSearchIndexer");
|
||||
dictionaryDAO = (DictionaryDAO) ctx.getBean("dictionaryDAO");
|
||||
searcher = (SearchService) ctx.getBean("searchService");
|
||||
indexerAndSearcher = (LuceneIndexerAndSearcher) ctx.getBean("luceneIndexerAndSearcherFactory");
|
||||
categoryService = (CategoryService) ctx.getBean("categoryService");
|
||||
serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY);
|
||||
|
||||
createTestTypes();
|
||||
|
||||
TransactionService transactionService = serviceRegistry.getTransactionService();
|
||||
UserTransaction tx = transactionService.getUserTransaction();
|
||||
tx.begin();
|
||||
|
||||
StoreRef storeRef = nodeService.createStore(
|
||||
StoreRef.PROTOCOL_WORKSPACE,
|
||||
"Test_" + System.currentTimeMillis());
|
||||
rootNodeRef = nodeService.getRootNode(storeRef);
|
||||
|
||||
n1 = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}one"), ContentModel.TYPE_CONTAINER).getChildRef();
|
||||
nodeService.setProperty(n1, QName.createQName("{namespace}property-1"), "value-1");
|
||||
n2 = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}two"), ContentModel.TYPE_CONTAINER).getChildRef();
|
||||
nodeService.setProperty(n2, QName.createQName("{namespace}property-1"), "value-1");
|
||||
nodeService.setProperty(n2, QName.createQName("{namespace}property-2"), "value-2");
|
||||
n3 = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}three"), ContentModel.TYPE_CONTAINER).getChildRef();
|
||||
n4 = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}four"), ContentModel.TYPE_CONTAINER).getChildRef();
|
||||
n5 = nodeService.createNode(n1, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}five"), ContentModel.TYPE_CONTAINER).getChildRef();
|
||||
n6 = nodeService.createNode(n1, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}six"), ContentModel.TYPE_CONTAINER).getChildRef();
|
||||
n7 = nodeService.createNode(n2, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}seven"), ContentModel.TYPE_CONTAINER).getChildRef();
|
||||
n8 = nodeService.createNode(n2, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}eight-2"), ContentModel.TYPE_CONTAINER).getChildRef();
|
||||
n9 = nodeService.createNode(n5, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}nine"), ContentModel.TYPE_CONTAINER).getChildRef();
|
||||
n10 = nodeService.createNode(n5, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}ten"), ContentModel.TYPE_CONTAINER).getChildRef();
|
||||
n11 = nodeService.createNode(n5, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}eleven"), ContentModel.TYPE_CONTAINER).getChildRef();
|
||||
n12 = nodeService.createNode(n5, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}twelve"), ContentModel.TYPE_CONTAINER).getChildRef();
|
||||
n13 = nodeService.createNode(n12, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}thirteen"), ContentModel.TYPE_CONTAINER).getChildRef();
|
||||
n14 = nodeService.createNode(n13, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}fourteen"), ContentModel.TYPE_CONTAINER).getChildRef();
|
||||
|
||||
nodeService.addChild(rootNodeRef, n8, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}eight-0"));
|
||||
nodeService.addChild(n1, n8, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}eight-1"));
|
||||
nodeService.addChild(n2, n13, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}link"));
|
||||
|
||||
nodeService.addChild(n1, n14, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}common"));
|
||||
nodeService.addChild(n2, n14, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}common"));
|
||||
nodeService.addChild(n5, n14, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}common"));
|
||||
nodeService.addChild(n6, n14, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}common"));
|
||||
nodeService.addChild(n12, n14, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}common"));
|
||||
nodeService.addChild(n13, n14, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}common"));
|
||||
|
||||
// Categories
|
||||
|
||||
catContainer = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "categoryContainer"), ContentModel.TYPE_CONTAINER).getChildRef();
|
||||
catRoot = nodeService.createNode(catContainer, ContentModel.ASSOC_CHILDREN, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "categoryRoot"), ContentModel.TYPE_CATEGORYROOT).getChildRef();
|
||||
|
||||
|
||||
|
||||
catRBase = nodeService.createNode(catRoot, ContentModel.ASSOC_CATEGORIES, QName.createQName(TEST_NAMESPACE, "Region"), ContentModel.TYPE_CATEGORY).getChildRef();
|
||||
catROne = nodeService.createNode(catRBase, ContentModel.ASSOC_SUBCATEGORIES, QName.createQName(TEST_NAMESPACE, "Europe"), ContentModel.TYPE_CATEGORY).getChildRef();
|
||||
catRTwo = nodeService.createNode(catRBase, ContentModel.ASSOC_SUBCATEGORIES, QName.createQName(TEST_NAMESPACE, "RestOfWorld"), ContentModel.TYPE_CATEGORY).getChildRef();
|
||||
catRThree = nodeService.createNode(catRTwo, ContentModel.ASSOC_SUBCATEGORIES, QName.createQName(TEST_NAMESPACE, "US"), ContentModel.TYPE_CATEGORY).getChildRef();
|
||||
|
||||
nodeService.addChild(catRoot, catRBase, ContentModel.ASSOC_CATEGORIES, QName.createQName(TEST_NAMESPACE, "InvestmentRegion"));
|
||||
nodeService.addChild(catRoot, catRBase, ContentModel.ASSOC_CATEGORIES, QName.createQName(TEST_NAMESPACE, "MarketingRegion"));
|
||||
|
||||
|
||||
catACBase = nodeService.createNode(catRoot, ContentModel.ASSOC_CATEGORIES, QName.createQName(TEST_NAMESPACE, "AssetClass"), ContentModel.TYPE_CATEGORY).getChildRef();
|
||||
catACOne = nodeService.createNode(catACBase, ContentModel.ASSOC_SUBCATEGORIES, QName.createQName(TEST_NAMESPACE, "Fixed"), ContentModel.TYPE_CATEGORY).getChildRef();
|
||||
catACTwo = nodeService.createNode(catACBase, ContentModel.ASSOC_SUBCATEGORIES, QName.createQName(TEST_NAMESPACE, "Equity"), ContentModel.TYPE_CATEGORY).getChildRef();
|
||||
catACThree = nodeService.createNode(catACTwo, ContentModel.ASSOC_SUBCATEGORIES, QName.createQName(TEST_NAMESPACE, "SpecialEquity"), ContentModel.TYPE_CATEGORY).getChildRef();
|
||||
|
||||
|
||||
|
||||
nodeService.addAspect(n1, assetClassCategorisationQName, createMap("assetClass", catACBase));
|
||||
nodeService.addAspect(n1, regionCategorisationQName, createMap("region", catRBase));
|
||||
|
||||
nodeService.addAspect(n2, assetClassCategorisationQName, createMap("assetClass", catACOne));
|
||||
nodeService.addAspect(n3, assetClassCategorisationQName, createMap("assetClass", catACOne));
|
||||
nodeService.addAspect(n4, assetClassCategorisationQName, createMap("assetClass", catACOne));
|
||||
nodeService.addAspect(n5, assetClassCategorisationQName, createMap("assetClass", catACOne));
|
||||
nodeService.addAspect(n6, assetClassCategorisationQName, createMap("assetClass", catACOne));
|
||||
|
||||
nodeService.addAspect(n7, assetClassCategorisationQName, createMap("assetClass", catACTwo));
|
||||
nodeService.addAspect(n8, assetClassCategorisationQName, createMap("assetClass", catACTwo));
|
||||
nodeService.addAspect(n9, assetClassCategorisationQName, createMap("assetClass", catACTwo));
|
||||
nodeService.addAspect(n10, assetClassCategorisationQName, createMap("assetClass", catACTwo));
|
||||
nodeService.addAspect(n11, assetClassCategorisationQName, createMap("assetClass", catACTwo));
|
||||
|
||||
nodeService.addAspect(n12, assetClassCategorisationQName, createMap("assetClass", catACOne, catACTwo));
|
||||
nodeService.addAspect(n13, assetClassCategorisationQName, createMap("assetClass", catACOne, catACTwo, catACThree));
|
||||
nodeService.addAspect(n14, assetClassCategorisationQName, createMap("assetClass", catACOne, catACTwo));
|
||||
|
||||
nodeService.addAspect(n2, regionCategorisationQName, createMap("region", catROne));
|
||||
nodeService.addAspect(n3, regionCategorisationQName, createMap("region", catROne));
|
||||
nodeService.addAspect(n4, regionCategorisationQName, createMap("region", catRTwo));
|
||||
nodeService.addAspect(n5, regionCategorisationQName, createMap("region", catRTwo));
|
||||
|
||||
nodeService.addAspect(n5, investmentRegionCategorisationQName, createMap("investmentRegion", catRBase));
|
||||
nodeService.addAspect(n5, marketingRegionCategorisationQName, createMap("marketingRegion", catRBase));
|
||||
nodeService.addAspect(n6, investmentRegionCategorisationQName, createMap("investmentRegion", catRBase));
|
||||
nodeService.addAspect(n7, investmentRegionCategorisationQName, createMap("investmentRegion", catRBase));
|
||||
nodeService.addAspect(n8, investmentRegionCategorisationQName, createMap("investmentRegion", catRBase));
|
||||
nodeService.addAspect(n9, investmentRegionCategorisationQName, createMap("investmentRegion", catRBase));
|
||||
nodeService.addAspect(n10, marketingRegionCategorisationQName, createMap("marketingRegion", catRBase));
|
||||
nodeService.addAspect(n11, marketingRegionCategorisationQName, createMap("marketingRegion", catRBase));
|
||||
nodeService.addAspect(n12, marketingRegionCategorisationQName, createMap("marketingRegion", catRBase));
|
||||
nodeService.addAspect(n13, marketingRegionCategorisationQName, createMap("marketingRegion", catRBase));
|
||||
nodeService.addAspect(n14, marketingRegionCategorisationQName, createMap("marketingRegion", catRBase));
|
||||
|
||||
tx.commit();
|
||||
}
|
||||
|
||||
private HashMap<QName, Serializable> createMap(String name, NodeRef[] nodeRefs)
|
||||
{
|
||||
HashMap<QName, Serializable> map = new HashMap<QName, Serializable>();
|
||||
Serializable value = (Serializable) Arrays.asList(nodeRefs);
|
||||
map.put(QName.createQName(TEST_NAMESPACE, name), value);
|
||||
return map;
|
||||
}
|
||||
|
||||
private HashMap<QName, Serializable> createMap(String name, NodeRef nodeRef)
|
||||
{
|
||||
return createMap(name, new NodeRef[]{nodeRef});
|
||||
}
|
||||
|
||||
private HashMap<QName, Serializable> createMap(String name, NodeRef nodeRef1, NodeRef nodeRef2)
|
||||
{
|
||||
return createMap(name, new NodeRef[]{nodeRef1, nodeRef2});
|
||||
}
|
||||
|
||||
private HashMap<QName, Serializable> createMap(String name, NodeRef nodeRef1, NodeRef nodeRef2, NodeRef nodeRef3)
|
||||
{
|
||||
return createMap(name, new NodeRef[]{nodeRef1, nodeRef2, nodeRef3});
|
||||
}
|
||||
|
||||
private void createTestTypes()
|
||||
{
|
||||
M2Model model = M2Model.createModel("test:lucenecategory");
|
||||
model.createNamespace(TEST_NAMESPACE, "test");
|
||||
model.createImport(NamespaceService.DICTIONARY_MODEL_1_0_URI, NamespaceService.DICTIONARY_MODEL_PREFIX);
|
||||
model.createImport(NamespaceService.CONTENT_MODEL_1_0_URI, NamespaceService.CONTENT_MODEL_PREFIX);
|
||||
|
||||
regionCategorisationQName = QName.createQName(TEST_NAMESPACE, "Region");
|
||||
M2Aspect generalCategorisation = model.createAspect("test:" + regionCategorisationQName.getLocalName());
|
||||
generalCategorisation.setParentName("cm:" + ContentModel.ASPECT_CLASSIFIABLE.getLocalName());
|
||||
M2Property genCatProp = generalCategorisation.createProperty("test:region");
|
||||
genCatProp.setIndexed(true);
|
||||
genCatProp.setIndexedAtomically(true);
|
||||
genCatProp.setMandatory(true);
|
||||
genCatProp.setMultiValued(true);
|
||||
genCatProp.setStoredInIndex(true);
|
||||
genCatProp.setTokenisedInIndex(true);
|
||||
genCatProp.setType("d:" + DataTypeDefinition.CATEGORY.getLocalName());
|
||||
|
||||
assetClassCategorisationQName = QName.createQName(TEST_NAMESPACE, "AssetClass");
|
||||
M2Aspect assetClassCategorisation = model.createAspect("test:" + assetClassCategorisationQName.getLocalName());
|
||||
assetClassCategorisation.setParentName("cm:" + ContentModel.ASPECT_CLASSIFIABLE.getLocalName());
|
||||
M2Property acProp = assetClassCategorisation.createProperty("test:assetClass");
|
||||
acProp.setIndexed(true);
|
||||
acProp.setIndexedAtomically(true);
|
||||
acProp.setMandatory(true);
|
||||
acProp.setMultiValued(true);
|
||||
acProp.setStoredInIndex(true);
|
||||
acProp.setTokenisedInIndex(true);
|
||||
acProp.setType("d:" + DataTypeDefinition.CATEGORY.getLocalName());
|
||||
|
||||
investmentRegionCategorisationQName = QName.createQName(TEST_NAMESPACE, "InvestmentRegion");
|
||||
M2Aspect investmentRegionCategorisation = model.createAspect("test:" + investmentRegionCategorisationQName.getLocalName());
|
||||
investmentRegionCategorisation.setParentName("cm:" + ContentModel.ASPECT_CLASSIFIABLE.getLocalName());
|
||||
M2Property irProp = investmentRegionCategorisation.createProperty("test:investmentRegion");
|
||||
irProp.setIndexed(true);
|
||||
irProp.setIndexedAtomically(true);
|
||||
irProp.setMandatory(true);
|
||||
irProp.setMultiValued(true);
|
||||
irProp.setStoredInIndex(true);
|
||||
irProp.setTokenisedInIndex(true);
|
||||
irProp.setType("d:" + DataTypeDefinition.CATEGORY.getLocalName());
|
||||
|
||||
marketingRegionCategorisationQName = QName.createQName(TEST_NAMESPACE, "MarketingRegion");
|
||||
M2Aspect marketingRegionCategorisation = model.createAspect("test:" + marketingRegionCategorisationQName.getLocalName());
|
||||
marketingRegionCategorisation.setParentName("cm:" + ContentModel.ASPECT_CLASSIFIABLE.getLocalName());
|
||||
M2Property mrProp = marketingRegionCategorisation.createProperty("test:marketingRegion");
|
||||
mrProp.setIndexed(true);
|
||||
mrProp.setIndexedAtomically(true);
|
||||
mrProp.setMandatory(true);
|
||||
mrProp.setMultiValued(true);
|
||||
mrProp.setStoredInIndex(true);
|
||||
mrProp.setTokenisedInIndex(true);
|
||||
mrProp.setType("d:" + DataTypeDefinition.CATEGORY.getLocalName());
|
||||
|
||||
dictionaryDAO.putModel(model);
|
||||
}
|
||||
|
||||
private void buildBaseIndex()
|
||||
{
|
||||
LuceneIndexerImpl indexer = LuceneIndexerImpl.getUpdateIndexer(rootNodeRef.getStoreRef(), "delta" + System.currentTimeMillis() + "_" + (new Random().nextInt()), indexerAndSearcher);
|
||||
indexer.setNodeService(nodeService);
|
||||
indexer.setLuceneIndexLock(luceneIndexLock);
|
||||
indexer.setDictionaryService(dictionaryService);
|
||||
indexer.setLuceneFullTextSearchIndexer(luceneFTS);
|
||||
//indexer.clearIndex();
|
||||
indexer.createNode(new ChildAssociationRef(null, null, null, rootNodeRef));
|
||||
indexer.createNode(new ChildAssociationRef(ContentModel.ASSOC_CHILDREN, rootNodeRef, QName.createQName("{namespace}one"), n1));
|
||||
indexer.createNode(new ChildAssociationRef(ContentModel.ASSOC_CHILDREN, rootNodeRef, QName.createQName("{namespace}two"), n2));
|
||||
indexer.createNode(new ChildAssociationRef(ContentModel.ASSOC_CHILDREN, rootNodeRef, QName.createQName("{namespace}three"), n3));
|
||||
indexer.createNode(new ChildAssociationRef(ContentModel.ASSOC_CHILDREN, rootNodeRef, QName.createQName("{namespace}four"), n4));
|
||||
|
||||
indexer.createNode(new ChildAssociationRef(ContentModel.ASSOC_CHILDREN, rootNodeRef, QName.createQName("{namespace}categoryContainer"), catContainer));
|
||||
indexer.createNode(new ChildAssociationRef(ContentModel.ASSOC_CHILDREN, catContainer, QName.createQName("{cat}categoryRoot"), catRoot));
|
||||
indexer.createNode(new ChildAssociationRef(ContentModel.ASSOC_CATEGORIES, catRoot, QName.createQName(TEST_NAMESPACE, "AssetClass"), catACBase));
|
||||
indexer.createNode(new ChildAssociationRef(ContentModel.ASSOC_SUBCATEGORIES, catACBase, QName.createQName(TEST_NAMESPACE, "Fixed"), catACOne));
|
||||
indexer.createNode(new ChildAssociationRef(ContentModel.ASSOC_SUBCATEGORIES, catACBase, QName.createQName(TEST_NAMESPACE, "Equity"), catACTwo));
|
||||
indexer.createNode(new ChildAssociationRef(ContentModel.ASSOC_SUBCATEGORIES, catACTwo, QName.createQName(TEST_NAMESPACE, "SpecialEquity"), catACThree));
|
||||
|
||||
indexer.createNode(new ChildAssociationRef(ContentModel.ASSOC_CATEGORIES, catRoot, QName.createQName(TEST_NAMESPACE, "Region"), catRBase));
|
||||
indexer.createNode(new ChildAssociationRef(ContentModel.ASSOC_SUBCATEGORIES, catRBase, QName.createQName(TEST_NAMESPACE, "Europe"), catROne));
|
||||
indexer.createNode(new ChildAssociationRef(ContentModel.ASSOC_SUBCATEGORIES, catRBase, QName.createQName(TEST_NAMESPACE, "RestOfWorld"), catRTwo));
|
||||
indexer.createNode(new ChildAssociationRef(ContentModel.ASSOC_SUBCATEGORIES, catRTwo, QName.createQName(TEST_NAMESPACE, "US"), catRThree));
|
||||
|
||||
indexer.createNode(new ChildAssociationRef(ContentModel.ASSOC_CATEGORIES, n1, QName.createQName("{namespace}five"), n5));
|
||||
indexer.createNode(new ChildAssociationRef(ContentModel.ASSOC_CATEGORIES, n1, QName.createQName("{namespace}six"), n6));
|
||||
indexer.createNode(new ChildAssociationRef(ContentModel.ASSOC_CATEGORIES, n2, QName.createQName("{namespace}seven"), n7));
|
||||
indexer.createNode(new ChildAssociationRef(ContentModel.ASSOC_CATEGORIES, n2, QName.createQName("{namespace}eight"), n8));
|
||||
indexer.createNode(new ChildAssociationRef(ContentModel.ASSOC_CATEGORIES, n5, QName.createQName("{namespace}nine"), n9));
|
||||
indexer.createNode(new ChildAssociationRef(ContentModel.ASSOC_CATEGORIES, n5, QName.createQName("{namespace}ten"), n10));
|
||||
indexer.createNode(new ChildAssociationRef(ContentModel.ASSOC_CATEGORIES, n5, QName.createQName("{namespace}eleven"), n11));
|
||||
indexer.createNode(new ChildAssociationRef(ContentModel.ASSOC_CATEGORIES, n5, QName.createQName("{namespace}twelve"), n12));
|
||||
indexer.createNode(new ChildAssociationRef(ContentModel.ASSOC_CATEGORIES, n12, QName.createQName("{namespace}thirteen"), n13));
|
||||
indexer.createNode(new ChildAssociationRef(ContentModel.ASSOC_CATEGORIES, n13, QName.createQName("{namespace}fourteen"), n14));
|
||||
indexer.prepare();
|
||||
indexer.commit();
|
||||
}
|
||||
|
||||
|
||||
public void testMulti() throws Exception
|
||||
{
|
||||
TransactionService transactionService = serviceRegistry.getTransactionService();
|
||||
UserTransaction tx = transactionService.getUserTransaction();
|
||||
tx.begin();
|
||||
buildBaseIndex();
|
||||
|
||||
LuceneSearcherImpl searcher = LuceneSearcherImpl.getSearcher(rootNodeRef.getStoreRef(), indexerAndSearcher);
|
||||
|
||||
searcher.setNodeService(nodeService);
|
||||
searcher.setDictionaryService(dictionaryService);
|
||||
searcher.setNamespacePrefixResolver(getNamespacePrefixReolsver(""));
|
||||
ResultSet results;
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"//*\" AND (PATH:\"/test:AssetClass/test:Equity/member\" PATH:\"/test:MarketingRegion/member\")", null, null);
|
||||
//printPaths(results);
|
||||
assertEquals(9, results.length());
|
||||
results.close();
|
||||
tx.rollback();
|
||||
}
|
||||
|
||||
public void testBasic() throws Exception
|
||||
{
|
||||
TransactionService transactionService = serviceRegistry.getTransactionService();
|
||||
UserTransaction tx = transactionService.getUserTransaction();
|
||||
tx.begin();
|
||||
buildBaseIndex();
|
||||
|
||||
LuceneSearcherImpl searcher = LuceneSearcherImpl.getSearcher(rootNodeRef.getStoreRef(), indexerAndSearcher);
|
||||
|
||||
searcher.setNodeService(nodeService);
|
||||
searcher.setDictionaryService(dictionaryService);
|
||||
searcher.setNamespacePrefixResolver(getNamespacePrefixReolsver(""));
|
||||
ResultSet results;
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/test:MarketingRegion\"", null, null);
|
||||
//printPaths(results);
|
||||
assertEquals(1, results.length());
|
||||
results.close();
|
||||
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/test:MarketingRegion//member\"", null, null);
|
||||
//printPaths(results);
|
||||
assertEquals(6, results.length());
|
||||
results.close();
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/cm:categoryContainer\"", null, null);
|
||||
assertEquals(1, results.length());
|
||||
results.close();
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/cm:categoryContainer/cm:categoryRoot\"", null, null);
|
||||
assertEquals(1, results.length());
|
||||
results.close();
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/cm:categoryContainer/cm:categoryRoot\"", null, null);
|
||||
assertEquals(1, results.length());
|
||||
results.close();
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/cm:categoryContainer/cm:categoryRoot/test:AssetClass\"", null, null);
|
||||
assertEquals(1, results.length());
|
||||
results.close();
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/cm:categoryContainer/cm:categoryRoot/test:AssetClass/member\" ", null, null);
|
||||
assertEquals(1, results.length());
|
||||
results.close();
|
||||
|
||||
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/cm:categoryContainer/cm:categoryRoot/test:AssetClass/test:Fixed\"", null, null);
|
||||
assertEquals(1, results.length());
|
||||
results.close();
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/cm:categoryContainer/cm:categoryRoot/test:AssetClass/test:Equity\"", null, null);
|
||||
assertEquals(1, results.length());
|
||||
results.close();
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/test:AssetClass\"", null, null);
|
||||
assertEquals(1, results.length());
|
||||
results.close();
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/test:AssetClass/test:Fixed\"", null, null);
|
||||
assertEquals(1, results.length());
|
||||
results.close();
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/test:AssetClass/test:Equity\"", null, null);
|
||||
assertEquals(1, results.length());
|
||||
results.close();
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/test:AssetClass/test:*\"", null, null);
|
||||
assertEquals(2, results.length());
|
||||
results.close();
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/test:AssetClass//test:*\"", null, null);
|
||||
assertEquals(3, results.length());
|
||||
results.close();
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/test:AssetClass/test:Fixed/member\"", null, null);
|
||||
//printPaths(results);
|
||||
assertEquals(8, results.length());
|
||||
results.close();
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/test:AssetClass/test:Equity/member\"", null, null);
|
||||
//printPaths(results);
|
||||
assertEquals(8, results.length());
|
||||
results.close();
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/test:AssetClass/test:Equity/test:SpecialEquity/member//.\"", null, null);
|
||||
assertEquals(1, results.length());
|
||||
results.close();
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/test:AssetClass/test:Equity/test:SpecialEquity/member//*\"", null, null);
|
||||
assertEquals(0, results.length());
|
||||
results.close();
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/test:AssetClass/test:Equity/test:SpecialEquity/member\"", null, null);
|
||||
assertEquals(1, results.length());
|
||||
results.close();
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "+PATH:\"/test:AssetClass/test:Equity/member\" AND +PATH:\"/test:AssetClass/test:Fixed/member\"", null, null);
|
||||
//printPaths(results);
|
||||
assertEquals(3, results.length());
|
||||
results.close();
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/test:AssetClass/test:Equity/member\" PATH:\"/test:AssetClass/test:Fixed/member\"", null, null);
|
||||
//printPaths(results);
|
||||
assertEquals(13, results.length());
|
||||
results.close();
|
||||
|
||||
// Region
|
||||
|
||||
assertEquals(4, nodeService.getChildAssocs(catRoot).size());
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/test:Region\"", null, null);
|
||||
//printPaths(results);
|
||||
assertEquals(1, results.length());
|
||||
results.close();
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/test:Region/member\"", null, null);
|
||||
//printPaths(results);
|
||||
assertEquals(1, results.length());
|
||||
results.close();
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/test:Region/test:Europe/member\"", null, null);
|
||||
//printPaths(results);
|
||||
assertEquals(2, results.length());
|
||||
results.close();
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/test:Region/test:RestOfWorld/member\"", null, null);
|
||||
//printPaths(results);
|
||||
assertEquals(2, results.length());
|
||||
results.close();
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/test:Region//member\"", null, null);
|
||||
//printPaths(results);
|
||||
assertEquals(5, results.length());
|
||||
results.close();
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/test:InvestmentRegion//member\"", null, null);
|
||||
//printPaths(results);
|
||||
assertEquals(5, results.length());
|
||||
results.close();
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/test:MarketingRegion//member\"", null, null);
|
||||
//printPaths(results);
|
||||
assertEquals(6, results.length());
|
||||
results.close();
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "+PATH:\"/test:AssetClass/test:Fixed/member\" AND +PATH:\"/test:Region/test:Europe/member\"", null, null);
|
||||
//printPaths(results);
|
||||
assertEquals(2, results.length());
|
||||
results.close();
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "+PATH:\"/cm:categoryContainer/cm:categoryRoot/test:AssetClass/test:Fixed/member\" AND +PATH:\"/cm:categoryContainer/cm:categoryRoot/test:Region/test:Europe/member\"", null, null);
|
||||
//printPaths(results);
|
||||
assertEquals(2, results.length());
|
||||
results.close();
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/test:AssetClass/test:Equity/member\" PATH:\"/test:MarketingRegion/member\"", null, null);
|
||||
//printPaths(results);
|
||||
assertEquals(9, results.length());
|
||||
results.close();
|
||||
tx.rollback();
|
||||
}
|
||||
|
||||
public void testCategoryServiceImpl() throws Exception
|
||||
{
|
||||
TransactionService transactionService = serviceRegistry.getTransactionService();
|
||||
UserTransaction tx = transactionService.getUserTransaction();
|
||||
tx.begin();
|
||||
buildBaseIndex();
|
||||
|
||||
LuceneSearcherImpl searcher = LuceneSearcherImpl.getSearcher(rootNodeRef.getStoreRef(), indexerAndSearcher);
|
||||
|
||||
searcher.setNodeService(nodeService);
|
||||
searcher.setDictionaryService(dictionaryService);
|
||||
searcher.setNamespacePrefixResolver(getNamespacePrefixReolsver(""));
|
||||
|
||||
ResultSet
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/cm:categoryContainer/cm:categoryRoot/test:AssetClass/*\" ", null, null);
|
||||
assertEquals(3, results.length());
|
||||
results.close();
|
||||
|
||||
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/cm:categoryContainer/cm:categoryRoot/test:AssetClass/member\" ", null, null);
|
||||
assertEquals(1, results.length());
|
||||
results.close();
|
||||
|
||||
LuceneCategoryServiceImpl impl = new LuceneCategoryServiceImpl();
|
||||
impl.setNodeService(nodeService);
|
||||
impl.setNamespacePrefixResolver(getNamespacePrefixReolsver(""));
|
||||
impl.setIndexerAndSearcher(indexerAndSearcher);
|
||||
impl.setDictionaryService(dictionaryService);
|
||||
|
||||
Collection<ChildAssociationRef>
|
||||
result = impl.getChildren(catACBase , CategoryService.Mode.MEMBERS, CategoryService.Depth.IMMEDIATE);
|
||||
assertEquals(1, result.size());
|
||||
|
||||
|
||||
result = impl.getChildren(catACBase , CategoryService.Mode.ALL, CategoryService.Depth.IMMEDIATE);
|
||||
assertEquals(3, result.size());
|
||||
|
||||
|
||||
result = impl.getChildren(catACBase , CategoryService.Mode.SUB_CATEGORIES, CategoryService.Depth.IMMEDIATE);
|
||||
assertEquals(2, result.size());
|
||||
|
||||
|
||||
result = impl.getChildren(catACBase , CategoryService.Mode.MEMBERS, CategoryService.Depth.ANY);
|
||||
assertEquals(18, result.size());
|
||||
|
||||
|
||||
result = impl.getChildren(catACBase , CategoryService.Mode.ALL, CategoryService.Depth.ANY);
|
||||
assertEquals(21, result.size());
|
||||
|
||||
|
||||
result = impl.getChildren(catACBase , CategoryService.Mode.SUB_CATEGORIES, CategoryService.Depth.ANY);
|
||||
assertEquals(3, result.size());
|
||||
|
||||
|
||||
result = impl.getClassifications(rootNodeRef.getStoreRef());
|
||||
assertEquals(4, result.size());
|
||||
|
||||
|
||||
result = impl.getCategories(rootNodeRef.getStoreRef(), QName.createQName(TEST_NAMESPACE, "AssetClass"), CategoryService.Depth.IMMEDIATE);
|
||||
assertEquals(2, result.size());
|
||||
|
||||
|
||||
Collection<QName> aspects = impl.getClassificationAspects();
|
||||
assertEquals(6, aspects.size());
|
||||
|
||||
tx.rollback();
|
||||
}
|
||||
|
||||
private NamespacePrefixResolver getNamespacePrefixReolsver(String defaultURI)
|
||||
{
|
||||
DynamicNamespacePrefixResolver nspr = new DynamicNamespacePrefixResolver(null);
|
||||
nspr.registerNamespace(NamespaceService.CONTENT_MODEL_PREFIX, NamespaceService.CONTENT_MODEL_1_0_URI);
|
||||
nspr.registerNamespace("namespace", "namespace");
|
||||
nspr.registerNamespace("test", TEST_NAMESPACE);
|
||||
nspr.registerNamespace(NamespaceService.DEFAULT_PREFIX, defaultURI);
|
||||
return nspr;
|
||||
}
|
||||
|
||||
public void testCategoryService() throws Exception
|
||||
{
|
||||
TransactionService transactionService = serviceRegistry.getTransactionService();
|
||||
UserTransaction tx = transactionService.getUserTransaction();
|
||||
tx.begin();
|
||||
buildBaseIndex();
|
||||
assertEquals(1, categoryService.getChildren(catACBase , CategoryService.Mode.MEMBERS, CategoryService.Depth.IMMEDIATE).size());
|
||||
assertEquals(2, categoryService.getChildren(catACBase , CategoryService.Mode.SUB_CATEGORIES, CategoryService.Depth.IMMEDIATE).size());
|
||||
assertEquals(3, categoryService.getChildren(catACBase , CategoryService.Mode.ALL, CategoryService.Depth.IMMEDIATE).size());
|
||||
assertEquals(18, categoryService.getChildren(catACBase , CategoryService.Mode.MEMBERS, CategoryService.Depth.ANY).size());
|
||||
assertEquals(3, categoryService.getChildren(catACBase , CategoryService.Mode.SUB_CATEGORIES, CategoryService.Depth.ANY).size());
|
||||
assertEquals(21, categoryService.getChildren(catACBase , CategoryService.Mode.ALL, CategoryService.Depth.ANY).size());
|
||||
assertEquals(4, categoryService.getClassifications(rootNodeRef.getStoreRef()).size());
|
||||
assertEquals(2, categoryService.getCategories(rootNodeRef.getStoreRef(), QName.createQName(TEST_NAMESPACE, "AssetClass"), CategoryService.Depth.IMMEDIATE).size());
|
||||
assertEquals(3, categoryService.getCategories(rootNodeRef.getStoreRef(), QName.createQName(TEST_NAMESPACE, "AssetClass"), CategoryService.Depth.ANY).size());
|
||||
assertEquals(6, categoryService.getClassificationAspects().size());
|
||||
assertEquals(2, categoryService.getRootCategories(rootNodeRef.getStoreRef(), QName.createQName(TEST_NAMESPACE, "AssetClass")).size());
|
||||
|
||||
NodeRef newRoot = categoryService.createRootCategory(rootNodeRef.getStoreRef(),QName.createQName(TEST_NAMESPACE, "AssetClass"), "Fruit");
|
||||
tx.commit();
|
||||
tx = transactionService.getUserTransaction();
|
||||
tx.begin();
|
||||
assertEquals(3, categoryService.getRootCategories(rootNodeRef.getStoreRef(), QName.createQName(TEST_NAMESPACE, "AssetClass")).size());
|
||||
assertEquals(3, categoryService.getCategories(rootNodeRef.getStoreRef(), QName.createQName(TEST_NAMESPACE, "AssetClass"), CategoryService.Depth.IMMEDIATE).size());
|
||||
assertEquals(4, categoryService.getCategories(rootNodeRef.getStoreRef(), QName.createQName(TEST_NAMESPACE, "AssetClass"), CategoryService.Depth.ANY).size());
|
||||
|
||||
NodeRef newCat = categoryService.createCategory(newRoot, "Banana");
|
||||
tx.commit();
|
||||
tx = transactionService.getUserTransaction();
|
||||
tx.begin();
|
||||
assertEquals(3, categoryService.getRootCategories(rootNodeRef.getStoreRef(), QName.createQName(TEST_NAMESPACE, "AssetClass")).size());
|
||||
assertEquals(3, categoryService.getCategories(rootNodeRef.getStoreRef(), QName.createQName(TEST_NAMESPACE, "AssetClass"), CategoryService.Depth.IMMEDIATE).size());
|
||||
assertEquals(5, categoryService.getCategories(rootNodeRef.getStoreRef(), QName.createQName(TEST_NAMESPACE, "AssetClass"), CategoryService.Depth.ANY).size());
|
||||
|
||||
categoryService.deleteCategory(newCat);
|
||||
tx.commit();
|
||||
tx = transactionService.getUserTransaction();
|
||||
tx.begin();
|
||||
assertEquals(3, categoryService.getRootCategories(rootNodeRef.getStoreRef(), QName.createQName(TEST_NAMESPACE, "AssetClass")).size());
|
||||
assertEquals(3, categoryService.getCategories(rootNodeRef.getStoreRef(), QName.createQName(TEST_NAMESPACE, "AssetClass"), CategoryService.Depth.IMMEDIATE).size());
|
||||
assertEquals(4, categoryService.getCategories(rootNodeRef.getStoreRef(), QName.createQName(TEST_NAMESPACE, "AssetClass"), CategoryService.Depth.ANY).size());
|
||||
|
||||
categoryService.deleteCategory(newRoot);
|
||||
tx.commit();
|
||||
tx = transactionService.getUserTransaction();
|
||||
tx.begin();
|
||||
assertEquals(2, categoryService.getRootCategories(rootNodeRef.getStoreRef(), QName.createQName(TEST_NAMESPACE, "AssetClass")).size());
|
||||
assertEquals(2, categoryService.getCategories(rootNodeRef.getStoreRef(), QName.createQName(TEST_NAMESPACE, "AssetClass"), CategoryService.Depth.IMMEDIATE).size());
|
||||
assertEquals(3, categoryService.getCategories(rootNodeRef.getStoreRef(), QName.createQName(TEST_NAMESPACE, "AssetClass"), CategoryService.Depth.ANY).size());
|
||||
|
||||
|
||||
tx.rollback();
|
||||
}
|
||||
|
||||
private int getTotalScore(ResultSet results)
|
||||
{
|
||||
int totalScore = 0;
|
||||
for(ResultSetRow row: results)
|
||||
{
|
||||
totalScore += row.getScore();
|
||||
}
|
||||
return totalScore;
|
||||
}
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene;
|
||||
|
||||
public interface LuceneConfig
|
||||
{
|
||||
|
||||
public String getIndexRootLocation();
|
||||
|
||||
public int getIndexerBatchSize();
|
||||
|
||||
public int getIndexerMaxMergeDocs();
|
||||
|
||||
public int getIndexerMergeFactor();
|
||||
|
||||
public int getIndexerMinMergeDocs();
|
||||
|
||||
public String getLockDirectory();
|
||||
|
||||
public int getQueryMaxClauses();
|
||||
|
||||
}
|
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.alfresco.repo.search.impl.lucene.LuceneIndexerAndSearcherFactory.LuceneIndexBackupComponent;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationComponent;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.transaction.TransactionService;
|
||||
import org.alfresco.util.ApplicationContextHelper;
|
||||
import org.alfresco.util.TempFileProvider;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* @see org.alfresco.repo.search.impl.lucene.LuceneIndexerAndSearcherFactory.LuceneIndexBackupComponent
|
||||
*
|
||||
* @author Derek Hulley
|
||||
*/
|
||||
public class LuceneIndexBackupComponentTest extends TestCase
|
||||
{
|
||||
private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
|
||||
|
||||
private LuceneIndexBackupComponent backupComponent;
|
||||
private File tempTargetDir;
|
||||
|
||||
private AuthenticationComponent authenticationComponent;
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception
|
||||
{
|
||||
TransactionService transactionService = (TransactionService) ctx.getBean("transactionComponent");
|
||||
NodeService nodeService = (NodeService) ctx.getBean("NodeService");
|
||||
LuceneIndexerAndSearcherFactory factory = (LuceneIndexerAndSearcherFactory) ctx.getBean("luceneIndexerAndSearcherFactory");
|
||||
|
||||
this.authenticationComponent = (AuthenticationComponent)ctx.getBean("authenticationComponent");
|
||||
this.authenticationComponent.setSystemUserAsCurrentUser();
|
||||
|
||||
tempTargetDir = new File(TempFileProvider.getTempDir(), getName());
|
||||
tempTargetDir.mkdir();
|
||||
|
||||
backupComponent = new LuceneIndexBackupComponent();
|
||||
backupComponent.setTransactionService(transactionService);
|
||||
backupComponent.setFactory(factory);
|
||||
backupComponent.setNodeService(nodeService);
|
||||
backupComponent.setTargetLocation(tempTargetDir.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception
|
||||
{
|
||||
authenticationComponent.clearCurrentSecurityContext();
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
public void testBackup()
|
||||
{
|
||||
backupComponent.backup();
|
||||
|
||||
// make sure that the target directory was created
|
||||
assertTrue("Target location doesn't exist", tempTargetDir.exists());
|
||||
}
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene;
|
||||
|
||||
import org.alfresco.repo.search.IndexerException;
|
||||
|
||||
/**
|
||||
* Exceptions relating to indexing within the lucene implementation
|
||||
*
|
||||
* @author andyh
|
||||
*
|
||||
*/
|
||||
public class LuceneIndexException extends IndexerException
|
||||
{
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 3688505480817422645L;
|
||||
|
||||
public LuceneIndexException(String message, Throwable cause)
|
||||
{
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public LuceneIndexException(String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.repo.search.Indexer;
|
||||
import org.alfresco.repo.search.impl.lucene.fts.FTSIndexerAware;
|
||||
import org.alfresco.repo.search.impl.lucene.fts.FullTextSearchIndexer;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
|
||||
/**
|
||||
* @author Andy Hind
|
||||
*/
|
||||
public interface LuceneIndexer extends Indexer, Lockable
|
||||
{
|
||||
|
||||
public void commit();
|
||||
public void rollback();
|
||||
public int prepare();
|
||||
public boolean isModified();
|
||||
public void setNodeService(NodeService nodeService);
|
||||
public void setDictionaryService(DictionaryService dictionaryService);
|
||||
public void setLuceneFullTextSearchIndexer(FullTextSearchIndexer luceneFullTextSearchIndexer);
|
||||
|
||||
public void updateFullTextSearch(int size);
|
||||
public void registerCallBack(FTSIndexerAware indexer);
|
||||
|
||||
public String getDeltaId();
|
||||
public void flushPending() throws LuceneIndexException;
|
||||
public Set<NodeRef> getDeletions();
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene;
|
||||
|
||||
import org.alfresco.repo.search.IndexerAndSearcher;
|
||||
import org.alfresco.repo.search.IndexerException;
|
||||
|
||||
public interface LuceneIndexerAndSearcher extends IndexerAndSearcher, LuceneConfig
|
||||
{
|
||||
public int prepare() throws IndexerException;
|
||||
public void commit() throws IndexerException;
|
||||
public void rollback();
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,340 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.alfresco.repo.search.SearcherException;
|
||||
import org.alfresco.repo.search.impl.lucene.query.PathQuery;
|
||||
import org.alfresco.service.cmr.dictionary.AspectDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.TypeDefinition;
|
||||
import org.alfresco.service.namespace.NamespacePrefixResolver;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.analysis.TokenStream;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.search.BooleanQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.RangeQuery;
|
||||
import org.apache.lucene.search.TermQuery;
|
||||
import org.saxpath.SAXPathException;
|
||||
|
||||
import com.werken.saxpath.XPathReader;
|
||||
|
||||
public class LuceneQueryParser extends QueryParser
|
||||
{
|
||||
private static Logger s_logger = Logger.getLogger(LuceneQueryParser.class);
|
||||
|
||||
private NamespacePrefixResolver namespacePrefixResolver;
|
||||
|
||||
private DictionaryService dictionaryService;
|
||||
|
||||
/**
|
||||
* Parses a query string, returning a {@link org.apache.lucene.search.Query}.
|
||||
*
|
||||
* @param query
|
||||
* the query string to be parsed.
|
||||
* @param field
|
||||
* the default field for query terms.
|
||||
* @param analyzer
|
||||
* used to find terms in the query text.
|
||||
* @throws ParseException
|
||||
* if the parsing fails
|
||||
*/
|
||||
static public Query parse(String query, String field, Analyzer analyzer,
|
||||
NamespacePrefixResolver namespacePrefixResolver, DictionaryService dictionaryService, int defaultOperator)
|
||||
throws ParseException
|
||||
{
|
||||
if (s_logger.isDebugEnabled())
|
||||
{
|
||||
s_logger.debug("Using Alfresco Lucene Query Parser for query: " + query);
|
||||
}
|
||||
LuceneQueryParser parser = new LuceneQueryParser(field, analyzer);
|
||||
parser.setOperator(defaultOperator);
|
||||
parser.setNamespacePrefixResolver(namespacePrefixResolver);
|
||||
parser.setDictionaryService(dictionaryService);
|
||||
return parser.parse(query);
|
||||
}
|
||||
|
||||
public void setNamespacePrefixResolver(NamespacePrefixResolver namespacePrefixResolver)
|
||||
{
|
||||
this.namespacePrefixResolver = namespacePrefixResolver;
|
||||
}
|
||||
|
||||
public LuceneQueryParser(String arg0, Analyzer arg1)
|
||||
{
|
||||
super(arg0, arg1);
|
||||
}
|
||||
|
||||
public LuceneQueryParser(CharStream arg0)
|
||||
{
|
||||
super(arg0);
|
||||
}
|
||||
|
||||
public LuceneQueryParser(QueryParserTokenManager arg0)
|
||||
{
|
||||
super(arg0);
|
||||
}
|
||||
|
||||
protected Query getFieldQuery(String field, String queryText) throws ParseException
|
||||
{
|
||||
try
|
||||
{
|
||||
if (field.equals("PATH"))
|
||||
{
|
||||
XPathReader reader = new XPathReader();
|
||||
LuceneXPathHandler handler = new LuceneXPathHandler();
|
||||
handler.setNamespacePrefixResolver(namespacePrefixResolver);
|
||||
handler.setDictionaryService(dictionaryService);
|
||||
reader.setXPathHandler(handler);
|
||||
reader.parse(queryText);
|
||||
PathQuery pathQuery = handler.getQuery();
|
||||
pathQuery.setRepeats(false);
|
||||
return pathQuery;
|
||||
}
|
||||
else if (field.equals("PATH_WITH_REPEATS"))
|
||||
{
|
||||
XPathReader reader = new XPathReader();
|
||||
LuceneXPathHandler handler = new LuceneXPathHandler();
|
||||
handler.setNamespacePrefixResolver(namespacePrefixResolver);
|
||||
handler.setDictionaryService(dictionaryService);
|
||||
reader.setXPathHandler(handler);
|
||||
reader.parse(queryText);
|
||||
PathQuery pathQuery = handler.getQuery();
|
||||
pathQuery.setRepeats(true);
|
||||
return pathQuery;
|
||||
}
|
||||
else if (field.equals("ID"))
|
||||
{
|
||||
TermQuery termQuery = new TermQuery(new Term(field, queryText));
|
||||
return termQuery;
|
||||
}
|
||||
else if (field.equals("TX"))
|
||||
{
|
||||
TermQuery termQuery = new TermQuery(new Term(field, queryText));
|
||||
return termQuery;
|
||||
}
|
||||
else if (field.equals("PARENT"))
|
||||
{
|
||||
TermQuery termQuery = new TermQuery(new Term(field, queryText));
|
||||
return termQuery;
|
||||
}
|
||||
else if (field.equals("PRIMARYPARENT"))
|
||||
{
|
||||
TermQuery termQuery = new TermQuery(new Term(field, queryText));
|
||||
return termQuery;
|
||||
}
|
||||
else if (field.equals("QNAME"))
|
||||
{
|
||||
XPathReader reader = new XPathReader();
|
||||
LuceneXPathHandler handler = new LuceneXPathHandler();
|
||||
handler.setNamespacePrefixResolver(namespacePrefixResolver);
|
||||
handler.setDictionaryService(dictionaryService);
|
||||
reader.setXPathHandler(handler);
|
||||
reader.parse("//" + queryText);
|
||||
return handler.getQuery();
|
||||
}
|
||||
else if (field.equals("TYPE"))
|
||||
{
|
||||
TypeDefinition target = dictionaryService.getType(QName.createQName(queryText));
|
||||
if (target == null)
|
||||
{
|
||||
throw new SearcherException("Invalid type: " + queryText);
|
||||
}
|
||||
QName targetQName = target.getName();
|
||||
HashSet<QName> subclasses = new HashSet<QName>();
|
||||
for (QName classRef : dictionaryService.getAllTypes())
|
||||
{
|
||||
TypeDefinition current = dictionaryService.getType(classRef);
|
||||
while ((current != null) && !current.getName().equals(targetQName))
|
||||
{
|
||||
current = (current.getParentName() == null) ? null : dictionaryService.getType(current
|
||||
.getParentName());
|
||||
}
|
||||
if (current != null)
|
||||
{
|
||||
subclasses.add(classRef);
|
||||
}
|
||||
}
|
||||
BooleanQuery booleanQuery = new BooleanQuery();
|
||||
for (QName qname : subclasses)
|
||||
{
|
||||
TermQuery termQuery = new TermQuery(new Term(field, qname.toString()));
|
||||
booleanQuery.add(termQuery, false, false);
|
||||
}
|
||||
return booleanQuery;
|
||||
}
|
||||
else if (field.equals("ASPECT"))
|
||||
{
|
||||
AspectDefinition target = dictionaryService.getAspect(QName.createQName(queryText));
|
||||
QName targetQName = target.getName();
|
||||
HashSet<QName> subclasses = new HashSet<QName>();
|
||||
for (QName classRef : dictionaryService.getAllAspects())
|
||||
{
|
||||
AspectDefinition current = dictionaryService.getAspect(classRef);
|
||||
while ((current != null) && !current.getName().equals(targetQName))
|
||||
{
|
||||
current = (current.getParentName() == null) ? null : dictionaryService.getAspect(current
|
||||
.getParentName());
|
||||
}
|
||||
if (current != null)
|
||||
{
|
||||
subclasses.add(classRef);
|
||||
}
|
||||
}
|
||||
|
||||
BooleanQuery booleanQuery = new BooleanQuery();
|
||||
for (QName qname : subclasses)
|
||||
{
|
||||
TermQuery termQuery = new TermQuery(new Term(field, qname.toString()));
|
||||
booleanQuery.add(termQuery, false, false);
|
||||
}
|
||||
return booleanQuery;
|
||||
}
|
||||
else if (field.startsWith("@"))
|
||||
{
|
||||
|
||||
String expandedFieldName = field;
|
||||
// Check for any prefixes and expand to the full uri
|
||||
if (field.charAt(1) != '{')
|
||||
{
|
||||
int colonPosition = field.indexOf(':');
|
||||
if (colonPosition == -1)
|
||||
{
|
||||
// use the default namespace
|
||||
expandedFieldName = "@{"
|
||||
+ namespacePrefixResolver.getNamespaceURI("") + "}" + field.substring(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// find the prefix
|
||||
expandedFieldName = "@{"
|
||||
+ namespacePrefixResolver.getNamespaceURI(field.substring(1, colonPosition)) + "}"
|
||||
+ field.substring(colonPosition + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if(expandedFieldName.endsWith(".mimetype"))
|
||||
{
|
||||
QName propertyQName = QName.createQName(expandedFieldName.substring(1, expandedFieldName.length()-9));
|
||||
PropertyDefinition propertyDef = dictionaryService.getProperty(propertyQName);
|
||||
if((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT)))
|
||||
{
|
||||
TermQuery termQuery = new TermQuery(new Term(expandedFieldName, queryText));
|
||||
return termQuery;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Already in expanded form
|
||||
return super.getFieldQuery(expandedFieldName, queryText);
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
return super.getFieldQuery(field, queryText);
|
||||
}
|
||||
}
|
||||
catch (SAXPathException e)
|
||||
{
|
||||
throw new ParseException("Failed to parse XPath...\n" + e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @exception ParseException
|
||||
* throw in overridden method to disallow
|
||||
*/
|
||||
protected Query getRangeQuery(String field, String part1, String part2, boolean inclusive) throws ParseException
|
||||
{
|
||||
if (field.startsWith("@"))
|
||||
{
|
||||
String fieldName = field;
|
||||
// Check for any prefixes and expand to the full uri
|
||||
if (field.charAt(1) != '{')
|
||||
{
|
||||
int colonPosition = field.indexOf(':');
|
||||
if (colonPosition == -1)
|
||||
{
|
||||
// use the default namespace
|
||||
fieldName = "@{" + namespacePrefixResolver.getNamespaceURI("") + "}" + field.substring(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// find the prefix
|
||||
fieldName = "@{"
|
||||
+ namespacePrefixResolver.getNamespaceURI(field.substring(1, colonPosition)) + "}"
|
||||
+ field.substring(colonPosition + 1);
|
||||
}
|
||||
}
|
||||
return new RangeQuery(new Term(fieldName, getToken(fieldName, part1)), new Term(fieldName, getToken(
|
||||
fieldName, part2)), inclusive);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
return super.getRangeQuery(field, part1, part2, inclusive);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private String getToken(String field, String value)
|
||||
{
|
||||
TokenStream source = analyzer.tokenStream(field, new StringReader(value));
|
||||
org.apache.lucene.analysis.Token t;
|
||||
String tokenised = null;
|
||||
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
t = source.next();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
t = null;
|
||||
}
|
||||
if (t == null)
|
||||
break;
|
||||
tokenised = t.termText();
|
||||
}
|
||||
try
|
||||
{
|
||||
source.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
|
||||
}
|
||||
return tokenised;
|
||||
|
||||
}
|
||||
|
||||
public void setDictionaryService(DictionaryService dictionaryService)
|
||||
{
|
||||
this.dictionaryService = dictionaryService;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.alfresco.repo.search.AbstractResultSet;
|
||||
import org.alfresco.repo.search.ResultSetRowIterator;
|
||||
import org.alfresco.repo.search.SearcherException;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.Path;
|
||||
import org.alfresco.service.cmr.search.ResultSetRow;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.search.Hits;
|
||||
import org.apache.lucene.search.Searcher;
|
||||
|
||||
/**
|
||||
* Implementation of a ResultSet on top of Lucene Hits class.
|
||||
*
|
||||
* @author andyh
|
||||
*
|
||||
*/
|
||||
public class LuceneResultSet extends AbstractResultSet
|
||||
{
|
||||
/**
|
||||
* The underlying hits
|
||||
*/
|
||||
Hits hits;
|
||||
|
||||
private Searcher searcher;
|
||||
|
||||
private NodeService nodeService;
|
||||
|
||||
/**
|
||||
* Wrap a lucene seach result with node support
|
||||
*
|
||||
* @param storeRef
|
||||
* @param hits
|
||||
*/
|
||||
public LuceneResultSet(Hits hits, Searcher searcher, NodeService nodeService, Path[]propertyPaths)
|
||||
{
|
||||
super(propertyPaths);
|
||||
this.hits = hits;
|
||||
this.searcher = searcher;
|
||||
this.nodeService = nodeService;
|
||||
}
|
||||
|
||||
/*
|
||||
* ResultSet implementation
|
||||
*/
|
||||
|
||||
public ResultSetRowIterator iterator()
|
||||
{
|
||||
return new LuceneResultSetRowIterator(this);
|
||||
}
|
||||
|
||||
public int length()
|
||||
{
|
||||
return hits.length();
|
||||
}
|
||||
|
||||
public NodeRef getNodeRef(int n)
|
||||
{
|
||||
try
|
||||
{
|
||||
// We have to get the document to resolve this
|
||||
// It is possible the store ref is also stored in the index
|
||||
Document doc = hits.doc(n);
|
||||
String id = doc.get("ID");
|
||||
return new NodeRef(id);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new SearcherException("IO Error reading reading node ref from the result set", e);
|
||||
}
|
||||
}
|
||||
|
||||
public float getScore(int n) throws SearcherException
|
||||
{
|
||||
try
|
||||
{
|
||||
return hits.score(n);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new SearcherException("IO Error reading score from the result set", e);
|
||||
}
|
||||
}
|
||||
|
||||
public Document getDocument(int n)
|
||||
{
|
||||
try
|
||||
{
|
||||
Document doc = hits.doc(n);
|
||||
return doc;
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new SearcherException("IO Error reading reading document from the result set", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void close()
|
||||
{
|
||||
try
|
||||
{
|
||||
searcher.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new SearcherException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public NodeService getNodeService()
|
||||
{
|
||||
return nodeService;
|
||||
}
|
||||
|
||||
public ResultSetRow getRow(int i)
|
||||
{
|
||||
if(i < length())
|
||||
{
|
||||
return new LuceneResultSetRow(this, i);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new SearcherException("Invalid row");
|
||||
}
|
||||
}
|
||||
|
||||
public ChildAssociationRef getChildAssocRef(int n)
|
||||
{
|
||||
return getRow(n).getChildAssocRef();
|
||||
}
|
||||
}
|
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.search.AbstractResultSetRow;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.Path;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
|
||||
/**
|
||||
* A row in a result set. Created on the fly.
|
||||
*
|
||||
* @author Andy Hind
|
||||
*
|
||||
*/
|
||||
public class LuceneResultSetRow extends AbstractResultSetRow
|
||||
{
|
||||
/**
|
||||
* The current document - cached so we do not get it for each value
|
||||
*/
|
||||
private Document document;
|
||||
|
||||
/**
|
||||
* Wrap a position in a lucene Hits class with node support
|
||||
*
|
||||
* @param resultSet
|
||||
* @param position
|
||||
*/
|
||||
public LuceneResultSetRow(LuceneResultSet resultSet, int index)
|
||||
{
|
||||
super(resultSet, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Support to cache the document for this row
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Document getDocument()
|
||||
{
|
||||
if (document == null)
|
||||
{
|
||||
document = ((LuceneResultSet) getResultSet()).getDocument(getIndex());
|
||||
}
|
||||
return document;
|
||||
}
|
||||
|
||||
/*
|
||||
* ResultSetRow implementation
|
||||
*/
|
||||
|
||||
protected Map<QName, Serializable> getDirectProperties()
|
||||
{
|
||||
LuceneResultSet lrs = (LuceneResultSet) getResultSet();
|
||||
return lrs.getNodeService().getProperties(lrs.getNodeRef(getIndex()));
|
||||
}
|
||||
|
||||
public Serializable getValue(Path path)
|
||||
{
|
||||
// TODO: implement path base look up against the document or via the
|
||||
// node service
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public QName getQName()
|
||||
{
|
||||
Field field = getDocument().getField("QNAME");
|
||||
if (field != null)
|
||||
{
|
||||
String qname = field.stringValue();
|
||||
if((qname == null) || (qname.length() == 0))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return QName.createQName(qname);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public QName getPrimaryAssocTypeQName()
|
||||
{
|
||||
|
||||
Field field = getDocument().getField("PRIMARYASSOCTYPEQNAME");
|
||||
if (field != null)
|
||||
{
|
||||
String qname = field.stringValue();
|
||||
return QName.createQName(qname);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ContentModel.ASSOC_CHILDREN;
|
||||
}
|
||||
}
|
||||
|
||||
public ChildAssociationRef getChildAssocRef()
|
||||
{
|
||||
Field field = getDocument().getField("PRIMARYPARENT");
|
||||
String primaryParent = null;
|
||||
if (field != null)
|
||||
{
|
||||
primaryParent = field.stringValue();
|
||||
}
|
||||
NodeRef childNodeRef = getNodeRef();
|
||||
NodeRef parentNodeRef = primaryParent == null ? null : new NodeRef(primaryParent);
|
||||
return new ChildAssociationRef(getPrimaryAssocTypeQName(), parentNodeRef, getQName(), childNodeRef);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene;
|
||||
|
||||
import org.alfresco.repo.search.AbstractResultSetRowIterator;
|
||||
import org.alfresco.service.cmr.search.ResultSetRow;
|
||||
|
||||
/**
|
||||
* Iterate over the rows in a LuceneResultSet
|
||||
*
|
||||
* @author andyh
|
||||
*
|
||||
*/
|
||||
public class LuceneResultSetRowIterator extends AbstractResultSetRowIterator
|
||||
{
|
||||
/**
|
||||
* Create an iterator over the result set. Follows standard ListIterator
|
||||
* conventions
|
||||
*
|
||||
* @param resultSet
|
||||
*/
|
||||
public LuceneResultSetRowIterator(LuceneResultSet resultSet)
|
||||
{
|
||||
super(resultSet);
|
||||
}
|
||||
|
||||
public ResultSetRow next()
|
||||
{
|
||||
return new LuceneResultSetRow((LuceneResultSet)getResultSet(), moveToNextPosition());
|
||||
}
|
||||
|
||||
public ResultSetRow previous()
|
||||
{
|
||||
return new LuceneResultSetRow((LuceneResultSet)getResultSet(), moveToPreviousPosition());
|
||||
}
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene;
|
||||
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.search.SearchService;
|
||||
import org.alfresco.service.namespace.NamespacePrefixResolver;
|
||||
|
||||
public interface LuceneSearcher extends SearchService, Lockable
|
||||
{
|
||||
public boolean indexExists();
|
||||
public void setNodeService(NodeService nodeService);
|
||||
public void setNamespacePrefixResolver(NamespacePrefixResolver namespacePrefixResolver);
|
||||
}
|
@@ -0,0 +1,652 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.repo.search.CannedQueryDef;
|
||||
import org.alfresco.repo.search.EmptyResultSet;
|
||||
import org.alfresco.repo.search.ISO9075;
|
||||
import org.alfresco.repo.search.Indexer;
|
||||
import org.alfresco.repo.search.QueryRegisterComponent;
|
||||
import org.alfresco.repo.search.SearcherException;
|
||||
import org.alfresco.repo.search.impl.NodeSearcher;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.Path;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.cmr.repository.XPathException;
|
||||
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
|
||||
import org.alfresco.service.cmr.search.QueryParameter;
|
||||
import org.alfresco.service.cmr.search.QueryParameterDefinition;
|
||||
import org.alfresco.service.cmr.search.ResultSet;
|
||||
import org.alfresco.service.cmr.search.SearchParameters;
|
||||
import org.alfresco.service.cmr.search.SearchService;
|
||||
import org.alfresco.service.namespace.NamespacePrefixResolver;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.util.SearchLanguageConversion;
|
||||
import org.apache.lucene.search.Hits;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.Searcher;
|
||||
import org.apache.lucene.search.Sort;
|
||||
import org.apache.lucene.search.SortField;
|
||||
import org.saxpath.SAXPathException;
|
||||
|
||||
import com.werken.saxpath.XPathReader;
|
||||
|
||||
/**
|
||||
* The Lucene implementation of Searcher At the moment we support only lucene
|
||||
* based queries.
|
||||
*
|
||||
* TODO: Support for other query languages
|
||||
*
|
||||
* @author andyh
|
||||
*
|
||||
*/
|
||||
public class LuceneSearcherImpl extends LuceneBase implements LuceneSearcher
|
||||
{
|
||||
/**
|
||||
* Default field name
|
||||
*/
|
||||
private static final String DEFAULT_FIELD = "TEXT";
|
||||
|
||||
private NamespacePrefixResolver namespacePrefixResolver;
|
||||
|
||||
private NodeService nodeService;
|
||||
|
||||
private DictionaryService dictionaryService;
|
||||
|
||||
private QueryRegisterComponent queryRegister;
|
||||
|
||||
private LuceneIndexer indexer;
|
||||
|
||||
/*
|
||||
* Searcher implementation
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get an initialised searcher for the store and transaction Normally we do
|
||||
* not search against a a store and delta. Currently only gets the searcher
|
||||
* against the main index.
|
||||
*
|
||||
* @param storeRef
|
||||
* @param deltaId
|
||||
* @return
|
||||
*/
|
||||
public static LuceneSearcherImpl getSearcher(StoreRef storeRef, LuceneIndexer indexer, LuceneConfig config)
|
||||
{
|
||||
LuceneSearcherImpl searcher = new LuceneSearcherImpl();
|
||||
searcher.setLuceneConfig(config);
|
||||
try
|
||||
{
|
||||
searcher.initialise(storeRef, indexer == null ? null : indexer.getDeltaId(), false, false);
|
||||
searcher.indexer = indexer;
|
||||
}
|
||||
catch (LuceneIndexException e)
|
||||
{
|
||||
throw new SearcherException(e);
|
||||
}
|
||||
return searcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an intialised searcher for the store. No transactional ammendsmends
|
||||
* are searched.
|
||||
*
|
||||
*
|
||||
* @param storeRef
|
||||
* @return
|
||||
*/
|
||||
public static LuceneSearcherImpl getSearcher(StoreRef storeRef, LuceneConfig config)
|
||||
{
|
||||
return getSearcher(storeRef, null, config);
|
||||
}
|
||||
|
||||
public void setNamespacePrefixResolver(NamespacePrefixResolver namespacePrefixResolver)
|
||||
{
|
||||
this.namespacePrefixResolver = namespacePrefixResolver;
|
||||
}
|
||||
|
||||
public boolean indexExists()
|
||||
{
|
||||
return mainIndexExists();
|
||||
}
|
||||
|
||||
public void setNodeService(NodeService nodeService)
|
||||
{
|
||||
this.nodeService = nodeService;
|
||||
}
|
||||
|
||||
public void setDictionaryService(DictionaryService dictionaryService)
|
||||
{
|
||||
this.dictionaryService = dictionaryService;
|
||||
}
|
||||
|
||||
public void setQueryRegister(QueryRegisterComponent queryRegister)
|
||||
{
|
||||
this.queryRegister = queryRegister;
|
||||
}
|
||||
|
||||
public ResultSet query(StoreRef store, String language, String queryString, Path[] queryOptions,
|
||||
QueryParameterDefinition[] queryParameterDefinitions) throws SearcherException
|
||||
{
|
||||
SearchParameters sp = new SearchParameters();
|
||||
sp.addStore(store);
|
||||
sp.setLanguage(language);
|
||||
sp.setQuery(queryString);
|
||||
if (queryOptions != null)
|
||||
{
|
||||
for (Path path : queryOptions)
|
||||
{
|
||||
sp.addAttrbutePath(path);
|
||||
}
|
||||
}
|
||||
if (queryParameterDefinitions != null)
|
||||
{
|
||||
for (QueryParameterDefinition qpd : queryParameterDefinitions)
|
||||
{
|
||||
sp.addQueryParameterDefinition(qpd);
|
||||
}
|
||||
}
|
||||
sp.excludeDataInTheCurrentTransaction(true);
|
||||
|
||||
return query(sp);
|
||||
}
|
||||
|
||||
public ResultSet query(SearchParameters searchParameters)
|
||||
{
|
||||
if (searchParameters.getStores().size() != 1)
|
||||
{
|
||||
throw new IllegalStateException("Only one store can be searched at present");
|
||||
}
|
||||
|
||||
String parameterisedQueryString;
|
||||
if (searchParameters.getQueryParameterDefinitions().size() > 0)
|
||||
{
|
||||
Map<QName, QueryParameterDefinition> map = new HashMap<QName, QueryParameterDefinition>();
|
||||
|
||||
for (QueryParameterDefinition qpd : searchParameters.getQueryParameterDefinitions())
|
||||
{
|
||||
map.put(qpd.getQName(), qpd);
|
||||
}
|
||||
|
||||
parameterisedQueryString = parameterise(searchParameters.getQuery(), map, null, namespacePrefixResolver);
|
||||
}
|
||||
else
|
||||
{
|
||||
parameterisedQueryString = searchParameters.getQuery();
|
||||
}
|
||||
|
||||
if (searchParameters.getLanguage().equalsIgnoreCase(SearchService.LANGUAGE_LUCENE))
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
int defaultOperator;
|
||||
if (searchParameters.getDefaultOperator() == SearchParameters.AND)
|
||||
{
|
||||
defaultOperator = LuceneQueryParser.DEFAULT_OPERATOR_AND;
|
||||
}
|
||||
else
|
||||
{
|
||||
defaultOperator = LuceneQueryParser.DEFAULT_OPERATOR_OR;
|
||||
}
|
||||
|
||||
Query query = LuceneQueryParser.parse(parameterisedQueryString, DEFAULT_FIELD, new LuceneAnalyser(
|
||||
dictionaryService), namespacePrefixResolver, dictionaryService, defaultOperator);
|
||||
Searcher searcher = getSearcher(indexer);
|
||||
if (searcher == null)
|
||||
{
|
||||
// no index return an empty result set
|
||||
return new EmptyResultSet();
|
||||
}
|
||||
|
||||
Hits hits;
|
||||
|
||||
if (searchParameters.getSortDefinitions().size() > 0)
|
||||
{
|
||||
int index = 0;
|
||||
SortField[] fields = new SortField[searchParameters.getSortDefinitions().size()];
|
||||
for (SearchParameters.SortDefinition sd : searchParameters.getSortDefinitions())
|
||||
{
|
||||
switch (sd.getSortType())
|
||||
{
|
||||
case FIELD:
|
||||
fields[index++] = new SortField(sd.getField(), !sd.isAscending());
|
||||
break;
|
||||
case DOCUMENT:
|
||||
fields[index++] = new SortField(null, SortField.DOC, !sd.isAscending());
|
||||
break;
|
||||
case SCORE:
|
||||
fields[index++] = new SortField(null, SortField.SCORE, !sd.isAscending());
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
hits = searcher.search(query, new Sort(fields));
|
||||
}
|
||||
else
|
||||
{
|
||||
hits = searcher.search(query);
|
||||
}
|
||||
|
||||
return new LuceneResultSet(hits, searcher, nodeService, searchParameters.getAttributePaths().toArray(
|
||||
new Path[0]));
|
||||
|
||||
}
|
||||
catch (ParseException e)
|
||||
{
|
||||
throw new SearcherException("Failed to parse query: " + parameterisedQueryString, e);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new SearcherException("IO exception during search", e);
|
||||
}
|
||||
}
|
||||
else if (searchParameters.getLanguage().equalsIgnoreCase(SearchService.LANGUAGE_XPATH))
|
||||
{
|
||||
try
|
||||
{
|
||||
XPathReader reader = new XPathReader();
|
||||
LuceneXPathHandler handler = new LuceneXPathHandler();
|
||||
handler.setNamespacePrefixResolver(namespacePrefixResolver);
|
||||
handler.setDictionaryService(dictionaryService);
|
||||
// TODO: Handler should have the query parameters to use in
|
||||
// building its lucene query
|
||||
// At the moment xpath style parameters in the PATH
|
||||
// expression are not supported.
|
||||
reader.setXPathHandler(handler);
|
||||
reader.parse(parameterisedQueryString);
|
||||
Query query = handler.getQuery();
|
||||
Searcher searcher = getSearcher(null);
|
||||
if (searcher == null)
|
||||
{
|
||||
// no index return an empty result set
|
||||
return new EmptyResultSet();
|
||||
}
|
||||
Hits hits = searcher.search(query);
|
||||
return new LuceneResultSet(hits, searcher, nodeService, searchParameters.getAttributePaths().toArray(
|
||||
new Path[0]));
|
||||
}
|
||||
catch (SAXPathException e)
|
||||
{
|
||||
throw new SearcherException("Failed to parse query: " + searchParameters.getQuery(), e);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new SearcherException("IO exception during search", e);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new SearcherException("Unknown query language: " + searchParameters.getLanguage());
|
||||
}
|
||||
}
|
||||
|
||||
public ResultSet query(StoreRef store, String language, String query)
|
||||
{
|
||||
return query(store, language, query, null, null);
|
||||
}
|
||||
|
||||
public ResultSet query(StoreRef store, String language, String query,
|
||||
QueryParameterDefinition[] queryParameterDefintions)
|
||||
{
|
||||
return query(store, language, query, null, queryParameterDefintions);
|
||||
}
|
||||
|
||||
public ResultSet query(StoreRef store, String language, String query, Path[] attributePaths)
|
||||
{
|
||||
return query(store, language, query, attributePaths, null);
|
||||
}
|
||||
|
||||
public ResultSet query(StoreRef store, QName queryId, QueryParameter[] queryParameters)
|
||||
{
|
||||
CannedQueryDef definition = queryRegister.getQueryDefinition(queryId);
|
||||
|
||||
// Do parameter replacement
|
||||
// As lucene phrases are tokensied it is correct to just do straight
|
||||
// string replacement.
|
||||
// The string will be formatted by the tokeniser.
|
||||
//
|
||||
// For non phrase queries this is incorrect but string replacement is
|
||||
// probably the best we can do.
|
||||
// As numbers and text are indexed specially, direct term queries only
|
||||
// make sense against textual data
|
||||
|
||||
checkParameters(definition, queryParameters);
|
||||
|
||||
String queryString = parameterise(definition.getQuery(), definition.getQueryParameterMap(), queryParameters,
|
||||
definition.getNamespacePrefixResolver());
|
||||
|
||||
return query(store, definition.getLanguage(), queryString, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* The definitions must provide a default value, or of not there must be a
|
||||
* parameter to provide the value
|
||||
*
|
||||
* @param definition
|
||||
* @param queryParameters
|
||||
* @throws QueryParameterisationException
|
||||
*/
|
||||
private void checkParameters(CannedQueryDef definition, QueryParameter[] queryParameters)
|
||||
throws QueryParameterisationException
|
||||
{
|
||||
List<QName> missing = new ArrayList<QName>();
|
||||
|
||||
Set<QName> parameterQNameSet = new HashSet<QName>();
|
||||
if (queryParameters != null)
|
||||
{
|
||||
for (QueryParameter parameter : queryParameters)
|
||||
{
|
||||
parameterQNameSet.add(parameter.getQName());
|
||||
}
|
||||
}
|
||||
|
||||
for (QueryParameterDefinition parameterDefinition : definition.getQueryParameterDefs())
|
||||
{
|
||||
if (!parameterDefinition.hasDefaultValue())
|
||||
{
|
||||
if (!parameterQNameSet.contains(parameterDefinition.getQName()))
|
||||
{
|
||||
missing.add(parameterDefinition.getQName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (missing.size() > 0)
|
||||
{
|
||||
StringBuilder buffer = new StringBuilder(128);
|
||||
buffer.append("The query is missing values for the following parameters: ");
|
||||
for (QName qName : missing)
|
||||
{
|
||||
buffer.append(qName);
|
||||
buffer.append(", ");
|
||||
}
|
||||
buffer.delete(buffer.length() - 1, buffer.length() - 1);
|
||||
buffer.delete(buffer.length() - 1, buffer.length() - 1);
|
||||
throw new QueryParameterisationException(buffer.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parameterise the query string - not sure if it is required to escape
|
||||
* lucence spacials chars The parameters could be used to build the query -
|
||||
* the contents of parameters should alread have been escaped if required.
|
||||
* ... mush better to provide the parameters and work out what to do TODO:
|
||||
* conditional query escapement - may be we should have a parameter type
|
||||
* that is not escaped
|
||||
*/
|
||||
private String parameterise(String unparameterised, Map<QName, QueryParameterDefinition> map,
|
||||
QueryParameter[] queryParameters, NamespacePrefixResolver nspr) throws QueryParameterisationException
|
||||
{
|
||||
|
||||
Map<QName, List<Serializable>> valueMap = new HashMap<QName, List<Serializable>>();
|
||||
|
||||
if (queryParameters != null)
|
||||
{
|
||||
for (QueryParameter parameter : queryParameters)
|
||||
{
|
||||
List<Serializable> list = valueMap.get(parameter.getQName());
|
||||
if (list == null)
|
||||
{
|
||||
list = new ArrayList<Serializable>();
|
||||
valueMap.put(parameter.getQName(), list);
|
||||
}
|
||||
list.add(parameter.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
Map<QName, ListIterator<Serializable>> iteratorMap = new HashMap<QName, ListIterator<Serializable>>();
|
||||
|
||||
List<QName> missing = new ArrayList<QName>(1);
|
||||
StringBuilder buffer = new StringBuilder(unparameterised);
|
||||
int index = 0;
|
||||
while ((index = buffer.indexOf("${", index)) != -1)
|
||||
{
|
||||
int endIndex = buffer.indexOf("}", index);
|
||||
String qNameString = buffer.substring(index + 2, endIndex);
|
||||
QName key = QName.createQName(qNameString, nspr);
|
||||
QueryParameterDefinition parameterDefinition = map.get(key);
|
||||
if (parameterDefinition == null)
|
||||
{
|
||||
missing.add(key);
|
||||
buffer.replace(index, endIndex + 1, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
ListIterator<Serializable> it = iteratorMap.get(key);
|
||||
if ((it == null) || (!it.hasNext()))
|
||||
{
|
||||
List<Serializable> list = valueMap.get(key);
|
||||
if ((list != null) && (list.size() > 0))
|
||||
{
|
||||
it = list.listIterator();
|
||||
}
|
||||
if (it != null)
|
||||
{
|
||||
iteratorMap.put(key, it);
|
||||
}
|
||||
}
|
||||
String value;
|
||||
if (it == null)
|
||||
{
|
||||
value = parameterDefinition.getDefault();
|
||||
}
|
||||
else
|
||||
{
|
||||
value = DefaultTypeConverter.INSTANCE.convert(String.class, it.next());
|
||||
}
|
||||
buffer.replace(index, endIndex + 1, value);
|
||||
}
|
||||
}
|
||||
if (missing.size() > 0)
|
||||
{
|
||||
StringBuilder error = new StringBuilder();
|
||||
error.append("The query uses the following parameters which are not defined: ");
|
||||
for (QName qName : missing)
|
||||
{
|
||||
error.append(qName);
|
||||
error.append(", ");
|
||||
}
|
||||
error.delete(error.length() - 1, error.length() - 1);
|
||||
error.delete(error.length() - 1, error.length() - 1);
|
||||
throw new QueryParameterisationException(error.toString());
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.repo.search.impl.NodeSearcher
|
||||
*/
|
||||
public List<NodeRef> selectNodes(NodeRef contextNodeRef, String xpath, QueryParameterDefinition[] parameters,
|
||||
NamespacePrefixResolver namespacePrefixResolver, boolean followAllParentLinks, String language)
|
||||
throws InvalidNodeRefException, XPathException
|
||||
{
|
||||
NodeSearcher nodeSearcher = new NodeSearcher(nodeService, dictionaryService, this);
|
||||
return nodeSearcher.selectNodes(contextNodeRef, xpath, parameters, namespacePrefixResolver,
|
||||
followAllParentLinks, language);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.repo.search.impl.NodeSearcher
|
||||
*/
|
||||
public List<Serializable> selectProperties(NodeRef contextNodeRef, String xpath,
|
||||
QueryParameterDefinition[] parameters, NamespacePrefixResolver namespacePrefixResolver,
|
||||
boolean followAllParentLinks, String language) throws InvalidNodeRefException, XPathException
|
||||
{
|
||||
NodeSearcher nodeSearcher = new NodeSearcher(nodeService, dictionaryService, this);
|
||||
return nodeSearcher.selectProperties(contextNodeRef, xpath, parameters, namespacePrefixResolver,
|
||||
followAllParentLinks, language);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns true if the pattern is present, otherwise false.
|
||||
*/
|
||||
public boolean contains(NodeRef nodeRef, QName propertyQName, String googleLikePattern)
|
||||
{
|
||||
return contains(nodeRef, propertyQName, googleLikePattern, SearchParameters.Operator.OR);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns true if the pattern is present, otherwise false.
|
||||
*/
|
||||
public boolean contains(NodeRef nodeRef, QName propertyQName, String googleLikePattern,
|
||||
SearchParameters.Operator defaultOperator)
|
||||
{
|
||||
ResultSet resultSet = null;
|
||||
try
|
||||
{
|
||||
// build Lucene search string specific to the node
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("+ID:\"").append(nodeRef.toString()).append("\" +(TEXT:(")
|
||||
.append(googleLikePattern.toLowerCase()).append(") ");
|
||||
if (propertyQName != null)
|
||||
{
|
||||
sb.append(" OR @").append(
|
||||
LuceneQueryParser.escape(QName.createQName(propertyQName.getNamespaceURI(),
|
||||
ISO9075.encode(propertyQName.getLocalName())).toString()));
|
||||
sb.append(":(").append(googleLikePattern.toLowerCase()).append(")");
|
||||
}
|
||||
else
|
||||
{
|
||||
for (QName key : nodeService.getProperties(nodeRef).keySet())
|
||||
{
|
||||
sb.append(" OR @").append(
|
||||
LuceneQueryParser.escape(QName.createQName(key.getNamespaceURI(),
|
||||
ISO9075.encode(key.getLocalName())).toString()));
|
||||
sb.append(":(").append(googleLikePattern.toLowerCase()).append(")");
|
||||
}
|
||||
}
|
||||
sb.append(")");
|
||||
|
||||
SearchParameters sp = new SearchParameters();
|
||||
sp.setLanguage(SearchService.LANGUAGE_LUCENE);
|
||||
sp.setQuery(sb.toString());
|
||||
sp.setDefaultOperator(defaultOperator);
|
||||
sp.addStore(nodeRef.getStoreRef());
|
||||
|
||||
resultSet = this.query(sp);
|
||||
boolean answer = resultSet.length() > 0;
|
||||
return answer;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (resultSet != null)
|
||||
{
|
||||
resultSet.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns true if the pattern is present, otherwise false.
|
||||
*
|
||||
* @see #setIndexer(Indexer)
|
||||
* @see #setSearcher(SearchService)
|
||||
*/
|
||||
public boolean like(NodeRef nodeRef, QName propertyQName, String sqlLikePattern, boolean includeFTS)
|
||||
{
|
||||
if (propertyQName == null)
|
||||
{
|
||||
throw new IllegalArgumentException("Property QName is mandatory for the like expression");
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder(sqlLikePattern.length() * 3);
|
||||
|
||||
if (includeFTS)
|
||||
{
|
||||
// convert the SQL-like pattern into a Lucene-compatible string
|
||||
String pattern = SearchLanguageConversion.convertXPathLikeToLucene(sqlLikePattern.toLowerCase());
|
||||
|
||||
// build Lucene search string specific to the node
|
||||
sb = new StringBuilder();
|
||||
sb.append("+ID:\"").append(nodeRef.toString()).append("\" +(");
|
||||
// FTS or attribute matches
|
||||
if (includeFTS)
|
||||
{
|
||||
sb.append("TEXT:(").append(pattern).append(") ");
|
||||
}
|
||||
if (propertyQName != null)
|
||||
{
|
||||
sb.append(" @").append(
|
||||
LuceneQueryParser.escape(QName.createQName(propertyQName.getNamespaceURI(),
|
||||
ISO9075.encode(propertyQName.getLocalName())).toString())).append(":(").append(pattern)
|
||||
.append(")");
|
||||
}
|
||||
sb.append(")");
|
||||
|
||||
ResultSet resultSet = null;
|
||||
try
|
||||
{
|
||||
resultSet = this.query(nodeRef.getStoreRef(), "lucene", sb.toString());
|
||||
boolean answer = resultSet.length() > 0;
|
||||
return answer;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (resultSet != null)
|
||||
{
|
||||
resultSet.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// convert the SQL-like pattern into a Lucene-compatible string
|
||||
String pattern = SearchLanguageConversion.convertXPathLikeToRegex(sqlLikePattern.toLowerCase());
|
||||
|
||||
Serializable property = nodeService.getProperty(nodeRef, propertyQName);
|
||||
if (property == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
String propertyString = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(
|
||||
nodeRef, propertyQName));
|
||||
return propertyString.toLowerCase().matches(pattern);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<NodeRef> selectNodes(NodeRef contextNodeRef, String xpath, QueryParameterDefinition[] parameters,
|
||||
NamespacePrefixResolver namespacePrefixResolver, boolean followAllParentLinks)
|
||||
throws InvalidNodeRefException, XPathException
|
||||
{
|
||||
return selectNodes(contextNodeRef, xpath, parameters, namespacePrefixResolver, followAllParentLinks,
|
||||
SearchService.LANGUAGE_XPATH);
|
||||
}
|
||||
|
||||
public List<Serializable> selectProperties(NodeRef contextNodeRef, String xpath,
|
||||
QueryParameterDefinition[] parameters, NamespacePrefixResolver namespacePrefixResolver,
|
||||
boolean followAllParentLinks) throws InvalidNodeRefException, XPathException
|
||||
{
|
||||
return selectProperties(contextNodeRef, xpath, parameters, namespacePrefixResolver, followAllParentLinks,
|
||||
SearchService.LANGUAGE_XPATH);
|
||||
}
|
||||
}
|
3133
source/java/org/alfresco/repo/search/impl/lucene/LuceneTest.java
Normal file
3133
source/java/org/alfresco/repo/search/impl/lucene/LuceneTest.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,187 @@
|
||||
<model name="test:lucenetest" xmlns="http://www.alfresco.org/model/dictionary/1.0">
|
||||
|
||||
<description>Test Model for Lucene tests</description>
|
||||
<author>Alfresco</author>
|
||||
<published>2005-07-13</published>
|
||||
<version>0.1</version>
|
||||
|
||||
<imports>
|
||||
<import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d"/>
|
||||
<import uri="http://www.alfresco.org/model/system/1.0" prefix="sys"/>
|
||||
</imports>
|
||||
|
||||
<namespaces>
|
||||
<namespace uri="http://www.alfresco.org/test/lucenetest" prefix="test"/>
|
||||
</namespaces>
|
||||
|
||||
<types>
|
||||
<type name="test:testSuperType">
|
||||
<title>Test Super Type</title>
|
||||
<parent>sys:container</parent>
|
||||
<associations>
|
||||
<child-association name="test:assoc">
|
||||
<source>
|
||||
<mandatory>false</mandatory>
|
||||
<many>true</many>
|
||||
</source>
|
||||
<target>
|
||||
<class>sys:base</class>
|
||||
<mandatory>false</mandatory>
|
||||
<many>true</many>
|
||||
</target>
|
||||
</child-association>
|
||||
</associations>
|
||||
</type>
|
||||
|
||||
<type name="test:testType">
|
||||
<title>Test Type</title>
|
||||
<parent>test:testSuperType</parent>
|
||||
<properties>
|
||||
<property name="test:text-indexed-stored-tokenised-atomic">
|
||||
<type>d:text</type>
|
||||
<mandatory>true</mandatory>
|
||||
<multiple>false</multiple>
|
||||
<index enabled="true">
|
||||
<atomic>true</atomic>
|
||||
<stored>true</stored>
|
||||
<tokenised>true</tokenised>
|
||||
</index>
|
||||
</property>
|
||||
<property name="test:text-indexed-unstored-tokenised-atomic">
|
||||
<type>d:text</type>
|
||||
<mandatory>true</mandatory>
|
||||
<multiple>false</multiple>
|
||||
<index enabled="true">
|
||||
<atomic>true</atomic>
|
||||
<stored>false</stored>
|
||||
<tokenised>true</tokenised>
|
||||
</index>
|
||||
</property>
|
||||
<property name="test:text-indexed-stored-tokenised-nonatomic">
|
||||
<type>d:text</type>
|
||||
<mandatory>true</mandatory>
|
||||
<multiple>false</multiple>
|
||||
<index enabled="true">
|
||||
<atomic>false</atomic>
|
||||
<stored>true</stored>
|
||||
<tokenised>true</tokenised>
|
||||
</index>
|
||||
</property>
|
||||
<property name="test:int-ista">
|
||||
<type>d:int</type>
|
||||
<mandatory>true</mandatory>
|
||||
<multiple>false</multiple>
|
||||
<index enabled="true">
|
||||
<atomic>true</atomic>
|
||||
<stored>true</stored>
|
||||
<tokenised>true</tokenised>
|
||||
</index>
|
||||
</property>
|
||||
<property name="test:long-ista">
|
||||
<type>d:long</type>
|
||||
<mandatory>true</mandatory>
|
||||
<multiple>false</multiple>
|
||||
<index enabled="true">
|
||||
<atomic>true</atomic>
|
||||
<stored>true</stored>
|
||||
<tokenised>true</tokenised>
|
||||
</index>
|
||||
</property>
|
||||
<property name="test:float-ista">
|
||||
<type>d:float</type>
|
||||
<mandatory>true</mandatory>
|
||||
<multiple>false</multiple>
|
||||
<index enabled="true">
|
||||
<atomic>true</atomic>
|
||||
<stored>true</stored>
|
||||
<tokenised>true</tokenised>
|
||||
</index>
|
||||
</property>
|
||||
<property name="test:double-ista">
|
||||
<type>d:double</type>
|
||||
<mandatory>true</mandatory>
|
||||
<multiple>false</multiple>
|
||||
<index enabled="true">
|
||||
<atomic>true</atomic>
|
||||
<stored>true</stored>
|
||||
<tokenised>true</tokenised>
|
||||
</index>
|
||||
</property>
|
||||
<property name="test:date-ista">
|
||||
<type>d:date</type>
|
||||
<mandatory>true</mandatory>
|
||||
<multiple>false</multiple>
|
||||
<index enabled="true">
|
||||
<atomic>true</atomic>
|
||||
<stored>true</stored>
|
||||
<tokenised>true</tokenised>
|
||||
</index>
|
||||
</property>
|
||||
<property name="test:datetime-ista">
|
||||
<type>d:datetime</type>
|
||||
<mandatory>true</mandatory>
|
||||
<multiple>false</multiple>
|
||||
<index enabled="true">
|
||||
<atomic>true</atomic>
|
||||
<stored>true</stored>
|
||||
<tokenised>true</tokenised>
|
||||
</index>
|
||||
</property>
|
||||
<property name="test:boolean-ista">
|
||||
<type>d:boolean</type>
|
||||
<mandatory>true</mandatory>
|
||||
<multiple>false</multiple>
|
||||
<index enabled="true">
|
||||
<atomic>true</atomic>
|
||||
<stored>true</stored>
|
||||
<tokenised>true</tokenised>
|
||||
</index>
|
||||
</property>
|
||||
<property name="test:qname-ista">
|
||||
<type>d:qname</type>
|
||||
<mandatory>true</mandatory>
|
||||
<multiple>false</multiple>
|
||||
<index enabled="true">
|
||||
<atomic>true</atomic>
|
||||
<stored>true</stored>
|
||||
<tokenised>true</tokenised>
|
||||
</index>
|
||||
</property>
|
||||
<property name="test:category-ista">
|
||||
<type>d:category</type>
|
||||
<mandatory>true</mandatory>
|
||||
<multiple>false</multiple>
|
||||
<index enabled="true">
|
||||
<atomic>true</atomic>
|
||||
<stored>true</stored>
|
||||
<tokenised>true</tokenised>
|
||||
</index>
|
||||
</property>
|
||||
<property name="test:noderef-ista">
|
||||
<type>d:noderef</type>
|
||||
<mandatory>true</mandatory>
|
||||
<multiple>false</multiple>
|
||||
<index enabled="true">
|
||||
<atomic>true</atomic>
|
||||
<stored>true</stored>
|
||||
<tokenised>true</tokenised>
|
||||
</index>
|
||||
</property>
|
||||
</properties>
|
||||
<mandatory-aspects>
|
||||
<aspect>test:testAspect</aspect>
|
||||
</mandatory-aspects>
|
||||
</type>
|
||||
</types>
|
||||
|
||||
<aspects>
|
||||
<aspect name="test:testSuperAspect">
|
||||
<title>Test Super Aspect</title>
|
||||
</aspect>
|
||||
<aspect name="test:testAspect">
|
||||
<title>Titled</title>
|
||||
<parent>test:testSuperAspect</parent>
|
||||
</aspect>
|
||||
</aspects>
|
||||
|
||||
</model>
|
@@ -0,0 +1,488 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.alfresco.repo.search.impl.lucene.analysis.PathTokenFilter;
|
||||
import org.alfresco.repo.search.impl.lucene.query.AbsoluteStructuredFieldPosition;
|
||||
import org.alfresco.repo.search.impl.lucene.query.DescendantAndSelfStructuredFieldPosition;
|
||||
import org.alfresco.repo.search.impl.lucene.query.PathQuery;
|
||||
import org.alfresco.repo.search.impl.lucene.query.RelativeStructuredFieldPosition;
|
||||
import org.alfresco.repo.search.impl.lucene.query.SelfAxisStructuredFieldPosition;
|
||||
import org.alfresco.repo.search.impl.lucene.query.StructuredFieldPosition;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.namespace.NamespacePrefixResolver;
|
||||
import org.saxpath.Axis;
|
||||
import org.saxpath.Operator;
|
||||
import org.saxpath.SAXPathException;
|
||||
import org.saxpath.XPathHandler;
|
||||
|
||||
public class LuceneXPathHandler implements XPathHandler
|
||||
{
|
||||
private PathQuery query;
|
||||
|
||||
private boolean isAbsolutePath = true;
|
||||
|
||||
int absolutePosition = 0;
|
||||
|
||||
private NamespacePrefixResolver namespacePrefixResolver;
|
||||
|
||||
private DictionaryService dictionaryService;
|
||||
|
||||
public LuceneXPathHandler()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
public PathQuery getQuery()
|
||||
{
|
||||
return this.query;
|
||||
}
|
||||
|
||||
public void endAbsoluteLocationPath() throws SAXPathException
|
||||
{
|
||||
// No action
|
||||
}
|
||||
|
||||
public void endAdditiveExpr(int op) throws SAXPathException
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case Operator.NO_OP:
|
||||
break;
|
||||
case Operator.ADD:
|
||||
case Operator.SUBTRACT:
|
||||
throw new UnsupportedOperationException();
|
||||
default:
|
||||
throw new UnsupportedOperationException("Unknown operation " + op);
|
||||
}
|
||||
}
|
||||
|
||||
public void endAllNodeStep() throws SAXPathException
|
||||
{
|
||||
// Nothing to do
|
||||
// Todo: Predicates
|
||||
}
|
||||
|
||||
public void endAndExpr(boolean create) throws SAXPathException
|
||||
{
|
||||
if (create)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
public void endCommentNodeStep() throws SAXPathException
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void endEqualityExpr(int op) throws SAXPathException
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case Operator.NO_OP:
|
||||
break;
|
||||
case Operator.EQUALS:
|
||||
case Operator.NOT_EQUALS:
|
||||
throw new UnsupportedOperationException();
|
||||
default:
|
||||
throw new UnsupportedOperationException("Unknown operation " + op);
|
||||
}
|
||||
}
|
||||
|
||||
public void endFilterExpr() throws SAXPathException
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void endFunction() throws SAXPathException
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void endMultiplicativeExpr(int op) throws SAXPathException
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case Operator.NO_OP:
|
||||
break;
|
||||
case Operator.MULTIPLY:
|
||||
case Operator.DIV:
|
||||
case Operator.MOD:
|
||||
throw new UnsupportedOperationException();
|
||||
default:
|
||||
throw new UnsupportedOperationException("Unknown operation " + op);
|
||||
}
|
||||
}
|
||||
|
||||
public void endNameStep() throws SAXPathException
|
||||
{
|
||||
// Do nothing at the moment
|
||||
// Could have repdicates
|
||||
}
|
||||
|
||||
public void endOrExpr(boolean create) throws SAXPathException
|
||||
{
|
||||
if (create)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
public void endPathExpr() throws SAXPathException
|
||||
{
|
||||
// Already built
|
||||
}
|
||||
|
||||
public void endPredicate() throws SAXPathException
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void endProcessingInstructionNodeStep() throws SAXPathException
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void endRelationalExpr(int op) throws SAXPathException
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case Operator.NO_OP:
|
||||
break;
|
||||
case Operator.GREATER_THAN:
|
||||
case Operator.GREATER_THAN_EQUALS:
|
||||
case Operator.LESS_THAN:
|
||||
case Operator.LESS_THAN_EQUALS:
|
||||
throw new UnsupportedOperationException();
|
||||
default:
|
||||
throw new UnsupportedOperationException("Unknown operation " + op);
|
||||
}
|
||||
}
|
||||
|
||||
public void endRelativeLocationPath() throws SAXPathException
|
||||
{
|
||||
// No action
|
||||
}
|
||||
|
||||
public void endTextNodeStep() throws SAXPathException
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void endUnaryExpr(int op) throws SAXPathException
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case Operator.NO_OP:
|
||||
break;
|
||||
case Operator.NEGATIVE:
|
||||
throw new UnsupportedOperationException();
|
||||
default:
|
||||
throw new UnsupportedOperationException("Unknown operation " + op);
|
||||
}
|
||||
}
|
||||
|
||||
public void endUnionExpr(boolean create) throws SAXPathException
|
||||
{
|
||||
if (create)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
public void endXPath() throws SAXPathException
|
||||
{
|
||||
// Do nothing at the moment
|
||||
}
|
||||
|
||||
public void literal(String arg0) throws SAXPathException
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void number(double arg0) throws SAXPathException
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void number(int arg0) throws SAXPathException
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void startAbsoluteLocationPath() throws SAXPathException
|
||||
{
|
||||
if (!isAbsolutePath)
|
||||
{
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void startAdditiveExpr() throws SAXPathException
|
||||
{
|
||||
// Do nothing at the moment
|
||||
}
|
||||
|
||||
public void startAllNodeStep(int axis) throws SAXPathException
|
||||
{
|
||||
switch (axis)
|
||||
{
|
||||
case Axis.CHILD:
|
||||
if (isAbsolutePath)
|
||||
{
|
||||
// addAbsolute(null, null);
|
||||
// We can always do relative stuff
|
||||
addRelative(null, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
addRelative(null, null);
|
||||
}
|
||||
break;
|
||||
case Axis.DESCENDANT_OR_SELF:
|
||||
query.appendQuery(getArrayList(new DescendantAndSelfStructuredFieldPosition(), new DescendantAndSelfStructuredFieldPosition()));
|
||||
break;
|
||||
case Axis.SELF:
|
||||
query.appendQuery(getArrayList(new SelfAxisStructuredFieldPosition(), new SelfAxisStructuredFieldPosition()));
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
private ArrayList<StructuredFieldPosition> getArrayList(StructuredFieldPosition one, StructuredFieldPosition two)
|
||||
{
|
||||
ArrayList<StructuredFieldPosition> answer = new ArrayList<StructuredFieldPosition>(2);
|
||||
answer.add(one);
|
||||
answer.add(two);
|
||||
return answer;
|
||||
}
|
||||
|
||||
public void startAndExpr() throws SAXPathException
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public void startCommentNodeStep(int arg0) throws SAXPathException
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void startEqualityExpr() throws SAXPathException
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public void startFilterExpr() throws SAXPathException
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void startFunction(String arg0, String arg1) throws SAXPathException
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void startMultiplicativeExpr() throws SAXPathException
|
||||
{
|
||||
// Do nothing at the moment
|
||||
}
|
||||
|
||||
public void startNameStep(int axis, String nameSpace, String localName) throws SAXPathException
|
||||
{
|
||||
switch (axis)
|
||||
{
|
||||
case Axis.CHILD:
|
||||
if (isAbsolutePath)
|
||||
{
|
||||
// addAbsolute(nameSpace, localName);
|
||||
// we can always do relative stuff
|
||||
addRelative(nameSpace, localName);
|
||||
}
|
||||
else
|
||||
{
|
||||
addRelative(nameSpace, localName);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void addAbsolute(String nameSpace, String localName)
|
||||
{
|
||||
ArrayList<StructuredFieldPosition> answer = new ArrayList<StructuredFieldPosition>(2);
|
||||
// TODO: Resolve name space
|
||||
absolutePosition++;
|
||||
if ((nameSpace == null) || (nameSpace.length() == 0))
|
||||
{
|
||||
|
||||
if(localName.equals("*"))
|
||||
{
|
||||
answer.add(new RelativeStructuredFieldPosition("*"));
|
||||
}
|
||||
else if (namespacePrefixResolver.getNamespaceURI("") == null)
|
||||
{
|
||||
answer.add(new AbsoluteStructuredFieldPosition(PathTokenFilter.NO_NS_TOKEN_TEXT, absolutePosition));
|
||||
}
|
||||
else
|
||||
{
|
||||
answer.add(new AbsoluteStructuredFieldPosition(namespacePrefixResolver.getNamespaceURI(""), absolutePosition));
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
answer.add(new AbsoluteStructuredFieldPosition(namespacePrefixResolver.getNamespaceURI(nameSpace), absolutePosition));
|
||||
}
|
||||
|
||||
absolutePosition++;
|
||||
if ((localName == null) || (localName.length() == 0))
|
||||
{
|
||||
answer.add(new AbsoluteStructuredFieldPosition("*", absolutePosition));
|
||||
}
|
||||
else
|
||||
{
|
||||
answer.add(new AbsoluteStructuredFieldPosition(localName, absolutePosition));
|
||||
}
|
||||
query.appendQuery(answer);
|
||||
|
||||
}
|
||||
|
||||
private void addRelative(String nameSpace, String localName)
|
||||
{
|
||||
ArrayList<StructuredFieldPosition> answer = new ArrayList<StructuredFieldPosition>(2);
|
||||
if ((nameSpace == null) || (nameSpace.length() == 0))
|
||||
{
|
||||
if(localName.equals("*"))
|
||||
{
|
||||
answer.add(new RelativeStructuredFieldPosition("*"));
|
||||
}
|
||||
else if (namespacePrefixResolver.getNamespaceURI("") == null)
|
||||
{
|
||||
answer.add(new RelativeStructuredFieldPosition(PathTokenFilter.NO_NS_TOKEN_TEXT));
|
||||
}
|
||||
else
|
||||
{
|
||||
answer.add(new RelativeStructuredFieldPosition(namespacePrefixResolver.getNamespaceURI("")));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
answer.add(new RelativeStructuredFieldPosition(namespacePrefixResolver.getNamespaceURI(nameSpace)));
|
||||
}
|
||||
|
||||
if ((localName == null) || (localName.length() == 0))
|
||||
{
|
||||
answer.add(new RelativeStructuredFieldPosition("*"));
|
||||
}
|
||||
else
|
||||
{
|
||||
answer.add(new RelativeStructuredFieldPosition(localName));
|
||||
}
|
||||
query.appendQuery(answer);
|
||||
}
|
||||
|
||||
public void startOrExpr() throws SAXPathException
|
||||
{
|
||||
// Do nothing at the moment
|
||||
}
|
||||
|
||||
public void startPathExpr() throws SAXPathException
|
||||
{
|
||||
// Just need one!
|
||||
}
|
||||
|
||||
public void startPredicate() throws SAXPathException
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void startProcessingInstructionNodeStep(int arg0, String arg1) throws SAXPathException
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void startRelationalExpr() throws SAXPathException
|
||||
{
|
||||
// Do nothing at the moment
|
||||
}
|
||||
|
||||
public void startRelativeLocationPath() throws SAXPathException
|
||||
{
|
||||
isAbsolutePath = false;
|
||||
}
|
||||
|
||||
public void startTextNodeStep(int arg0) throws SAXPathException
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void startUnaryExpr() throws SAXPathException
|
||||
{
|
||||
// Do nothing for now
|
||||
}
|
||||
|
||||
public void startUnionExpr() throws SAXPathException
|
||||
{
|
||||
// Do nothing at the moment
|
||||
}
|
||||
|
||||
public void startXPath() throws SAXPathException
|
||||
{
|
||||
query = new PathQuery(dictionaryService);
|
||||
}
|
||||
|
||||
public void variableReference(String uri, String localName) throws SAXPathException
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void setNamespacePrefixResolver(NamespacePrefixResolver namespacePrefixResolver)
|
||||
{
|
||||
this.namespacePrefixResolver = namespacePrefixResolver;
|
||||
}
|
||||
|
||||
public void setDictionaryService(DictionaryService dictionaryService)
|
||||
{
|
||||
this.dictionaryService = dictionaryService;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,192 @@
|
||||
/* Generated By:JavaCC: Do not edit this line. ParseException.java Version 3.0 */
|
||||
package org.alfresco.repo.search.impl.lucene;
|
||||
|
||||
/**
|
||||
* This exception is thrown when parse errors are encountered.
|
||||
* You can explicitly create objects of this exception type by
|
||||
* calling the method generateParseException in the generated
|
||||
* parser.
|
||||
*
|
||||
* You can modify this class to customize your error reporting
|
||||
* mechanisms so long as you retain the public fields.
|
||||
*/
|
||||
public class ParseException extends Exception {
|
||||
|
||||
/**
|
||||
* This constructor is used by the method "generateParseException"
|
||||
* in the generated parser. Calling this constructor generates
|
||||
* a new object of this type with the fields "currentToken",
|
||||
* "expectedTokenSequences", and "tokenImage" set. The boolean
|
||||
* flag "specialConstructor" is also set to true to indicate that
|
||||
* this constructor was used to create this object.
|
||||
* This constructor calls its super class with the empty string
|
||||
* to force the "toString" method of parent class "Throwable" to
|
||||
* print the error message in the form:
|
||||
* ParseException: <result of getMessage>
|
||||
*/
|
||||
public ParseException(Token currentTokenVal,
|
||||
int[][] expectedTokenSequencesVal,
|
||||
String[] tokenImageVal
|
||||
)
|
||||
{
|
||||
super("");
|
||||
specialConstructor = true;
|
||||
currentToken = currentTokenVal;
|
||||
expectedTokenSequences = expectedTokenSequencesVal;
|
||||
tokenImage = tokenImageVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* The following constructors are for use by you for whatever
|
||||
* purpose you can think of. Constructing the exception in this
|
||||
* manner makes the exception behave in the normal way - i.e., as
|
||||
* documented in the class "Throwable". The fields "errorToken",
|
||||
* "expectedTokenSequences", and "tokenImage" do not contain
|
||||
* relevant information. The JavaCC generated code does not use
|
||||
* these constructors.
|
||||
*/
|
||||
|
||||
public ParseException() {
|
||||
super();
|
||||
specialConstructor = false;
|
||||
}
|
||||
|
||||
public ParseException(String message) {
|
||||
super(message);
|
||||
specialConstructor = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This variable determines which constructor was used to create
|
||||
* this object and thereby affects the semantics of the
|
||||
* "getMessage" method (see below).
|
||||
*/
|
||||
protected boolean specialConstructor;
|
||||
|
||||
/**
|
||||
* This is the last token that has been consumed successfully. If
|
||||
* this object has been created due to a parse error, the token
|
||||
* followng this token will (therefore) be the first error token.
|
||||
*/
|
||||
public Token currentToken;
|
||||
|
||||
/**
|
||||
* Each entry in this array is an array of integers. Each array
|
||||
* of integers represents a sequence of tokens (by their ordinal
|
||||
* values) that is expected at this point of the parse.
|
||||
*/
|
||||
public int[][] expectedTokenSequences;
|
||||
|
||||
/**
|
||||
* This is a reference to the "tokenImage" array of the generated
|
||||
* parser within which the parse error occurred. This array is
|
||||
* defined in the generated ...Constants interface.
|
||||
*/
|
||||
public String[] tokenImage;
|
||||
|
||||
/**
|
||||
* This method has the standard behavior when this object has been
|
||||
* created using the standard constructors. Otherwise, it uses
|
||||
* "currentToken" and "expectedTokenSequences" to generate a parse
|
||||
* error message and returns it. If this object has been created
|
||||
* due to a parse error, and you do not catch it (it gets thrown
|
||||
* from the parser), then this method is called during the printing
|
||||
* of the final stack trace, and hence the correct error message
|
||||
* gets displayed.
|
||||
*/
|
||||
public String getMessage() {
|
||||
if (!specialConstructor) {
|
||||
return super.getMessage();
|
||||
}
|
||||
String expected = "";
|
||||
int maxSize = 0;
|
||||
for (int i = 0; i < expectedTokenSequences.length; i++) {
|
||||
if (maxSize < expectedTokenSequences[i].length) {
|
||||
maxSize = expectedTokenSequences[i].length;
|
||||
}
|
||||
for (int j = 0; j < expectedTokenSequences[i].length; j++) {
|
||||
expected += tokenImage[expectedTokenSequences[i][j]] + " ";
|
||||
}
|
||||
if (expectedTokenSequences[i][expectedTokenSequences[i].length - 1] != 0) {
|
||||
expected += "...";
|
||||
}
|
||||
expected += eol + " ";
|
||||
}
|
||||
String retval = "Encountered \"";
|
||||
Token tok = currentToken.next;
|
||||
for (int i = 0; i < maxSize; i++) {
|
||||
if (i != 0) retval += " ";
|
||||
if (tok.kind == 0) {
|
||||
retval += tokenImage[0];
|
||||
break;
|
||||
}
|
||||
retval += add_escapes(tok.image);
|
||||
tok = tok.next;
|
||||
}
|
||||
retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn;
|
||||
retval += "." + eol;
|
||||
if (expectedTokenSequences.length == 1) {
|
||||
retval += "Was expecting:" + eol + " ";
|
||||
} else {
|
||||
retval += "Was expecting one of:" + eol + " ";
|
||||
}
|
||||
retval += expected;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* The end of line string for this machine.
|
||||
*/
|
||||
protected String eol = System.getProperty("line.separator", "\n");
|
||||
|
||||
/**
|
||||
* Used to convert raw characters to their escaped version
|
||||
* when these raw version cannot be used as part of an ASCII
|
||||
* string literal.
|
||||
*/
|
||||
protected String add_escapes(String str) {
|
||||
StringBuffer retval = new StringBuffer();
|
||||
char ch;
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
switch (str.charAt(i))
|
||||
{
|
||||
case 0 :
|
||||
continue;
|
||||
case '\b':
|
||||
retval.append("\\b");
|
||||
continue;
|
||||
case '\t':
|
||||
retval.append("\\t");
|
||||
continue;
|
||||
case '\n':
|
||||
retval.append("\\n");
|
||||
continue;
|
||||
case '\f':
|
||||
retval.append("\\f");
|
||||
continue;
|
||||
case '\r':
|
||||
retval.append("\\r");
|
||||
continue;
|
||||
case '\"':
|
||||
retval.append("\\\"");
|
||||
continue;
|
||||
case '\'':
|
||||
retval.append("\\\'");
|
||||
continue;
|
||||
case '\\':
|
||||
retval.append("\\\\");
|
||||
continue;
|
||||
default:
|
||||
if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
|
||||
String s = "0000" + Integer.toString(ch, 16);
|
||||
retval.append("\\u" + s.substring(s.length() - 4, s.length()));
|
||||
} else {
|
||||
retval.append(ch);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return retval.toString();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
|
||||
public class QueryParameterisationException extends AlfrescoRuntimeException
|
||||
{
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public QueryParameterisationException(String msg)
|
||||
{
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public QueryParameterisationException(String msg, Throwable cause)
|
||||
{
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
}
|
1206
source/java/org/alfresco/repo/search/impl/lucene/QueryParser.java
Normal file
1206
source/java/org/alfresco/repo/search/impl/lucene/QueryParser.java
Normal file
File diff suppressed because it is too large
Load Diff
826
source/java/org/alfresco/repo/search/impl/lucene/QueryParser.jj
Normal file
826
source/java/org/alfresco/repo/search/impl/lucene/QueryParser.jj
Normal file
@@ -0,0 +1,826 @@
|
||||
/**
|
||||
* Copyright 2004 The Apache Software Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
options {
|
||||
STATIC=false;
|
||||
JAVA_UNICODE_ESCAPE=true;
|
||||
USER_CHAR_STREAM=true;
|
||||
}
|
||||
|
||||
PARSER_BEGIN(QueryParser)
|
||||
|
||||
package org.alfresco.repo.search.impl.lucene;
|
||||
|
||||
import java.util.Vector;
|
||||
import java.io.*;
|
||||
import java.text.*;
|
||||
import java.util.*;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.analysis.*;
|
||||
import org.apache.lucene.document.*;
|
||||
import org.apache.lucene.search.*;
|
||||
|
||||
/**
|
||||
* This class is generated by JavaCC. The only method that clients should need
|
||||
* to call is <a href="#parse">parse()</a>.
|
||||
*
|
||||
* The syntax for query strings is as follows:
|
||||
* A Query is a series of clauses.
|
||||
* A clause may be prefixed by:
|
||||
* <ul>
|
||||
* <li> a plus (<code>+</code>) or a minus (<code>-</code>) sign, indicating
|
||||
* that the clause is required or prohibited respectively; or
|
||||
* <li> a term followed by a colon, indicating the field to be searched.
|
||||
* This enables one to construct queries which search multiple fields.
|
||||
* </ul>
|
||||
*
|
||||
* A clause may be either:
|
||||
* <ul>
|
||||
* <li> a term, indicating all the documents that contain this term; or
|
||||
* <li> a nested query, enclosed in parentheses. Note that this may be used
|
||||
* with a <code>+</code>/<code>-</code> prefix to require any of a set of
|
||||
* terms.
|
||||
* </ul>
|
||||
*
|
||||
* Thus, in BNF, the query grammar is:
|
||||
* <pre>
|
||||
* Query ::= ( Clause )*
|
||||
* Clause ::= ["+", "-"] [<TERM> ":"] ( <TERM> | "(" Query ")" )
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* Examples of appropriately formatted queries can be found in the <a
|
||||
* href="http://jakarta.apache.org/lucene/src/test/org/apache/lucene/queryParser/TestQueryParser.java">test cases</a>.
|
||||
* </p>
|
||||
*
|
||||
* @author Brian Goetz
|
||||
* @author Peter Halacsy
|
||||
* @author Tatu Saloranta
|
||||
*/
|
||||
|
||||
public class QueryParser {
|
||||
|
||||
private static final int CONJ_NONE = 0;
|
||||
private static final int CONJ_AND = 1;
|
||||
private static final int CONJ_OR = 2;
|
||||
|
||||
private static final int MOD_NONE = 0;
|
||||
private static final int MOD_NOT = 10;
|
||||
private static final int MOD_REQ = 11;
|
||||
|
||||
public static final int DEFAULT_OPERATOR_OR = 0;
|
||||
public static final int DEFAULT_OPERATOR_AND = 1;
|
||||
|
||||
/** The actual operator that parser uses to combine query terms */
|
||||
private int operator = DEFAULT_OPERATOR_OR;
|
||||
|
||||
/**
|
||||
* Whether terms of wildcard and prefix queries are to be automatically
|
||||
* lower-cased or not. Default is <code>true</code>.
|
||||
*/
|
||||
boolean lowercaseWildcardTerms = true;
|
||||
|
||||
Analyzer analyzer;
|
||||
String field;
|
||||
int phraseSlop = 0;
|
||||
float fuzzyMinSim = FuzzyQuery.defaultMinSimilarity;
|
||||
Locale locale = Locale.getDefault();
|
||||
|
||||
/** Parses a query string, returning a {@link org.apache.lucene.search.Query}.
|
||||
* @param query the query string to be parsed.
|
||||
* @param field the default field for query terms.
|
||||
* @param analyzer used to find terms in the query text.
|
||||
* @throws ParseException if the parsing fails
|
||||
*/
|
||||
static public Query parse(String query, String field, Analyzer analyzer)
|
||||
throws ParseException {
|
||||
QueryParser parser = new QueryParser(field, analyzer);
|
||||
return parser.parse(query);
|
||||
}
|
||||
|
||||
/** Constructs a query parser.
|
||||
* @param f the default field for query terms.
|
||||
* @param a used to find terms in the query text.
|
||||
*/
|
||||
public QueryParser(String f, Analyzer a) {
|
||||
this(new FastCharStream(new StringReader("")));
|
||||
analyzer = a;
|
||||
field = f;
|
||||
}
|
||||
|
||||
/** Parses a query string, returning a
|
||||
* <a href="lucene.search.Query.html">Query</a>.
|
||||
* @param query the query string to be parsed.
|
||||
* @throws ParseException if the parsing fails
|
||||
*/
|
||||
public Query parse(String query) throws ParseException {
|
||||
ReInit(new FastCharStream(new StringReader(query)));
|
||||
try {
|
||||
return Query(field);
|
||||
}
|
||||
catch (TokenMgrError tme) {
|
||||
throw new ParseException(tme.getMessage());
|
||||
}
|
||||
catch (BooleanQuery.TooManyClauses tmc) {
|
||||
throw new ParseException("Too many boolean clauses");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the analyzer.
|
||||
*/
|
||||
public Analyzer getAnalyzer() {
|
||||
return analyzer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the field.
|
||||
*/
|
||||
public String getField() {
|
||||
return field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default minimal similarity for fuzzy queries.
|
||||
*/
|
||||
public float getFuzzyMinSim() {
|
||||
return fuzzyMinSim;
|
||||
}
|
||||
/**
|
||||
*Set the default minimum similarity for fuzzy queries.
|
||||
*/
|
||||
public void setFuzzyMinSim(float fuzzyMinSim) {
|
||||
this.fuzzyMinSim = fuzzyMinSim;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default slop for phrases. If zero, then exact phrase matches
|
||||
* are required. Default value is zero.
|
||||
*/
|
||||
public void setPhraseSlop(int phraseSlop) {
|
||||
this.phraseSlop = phraseSlop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default slop for phrases.
|
||||
*/
|
||||
public int getPhraseSlop() {
|
||||
return phraseSlop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the boolean operator of the QueryParser.
|
||||
* In classic mode (<code>DEFAULT_OPERATOR_OR</code>) terms without any modifiers
|
||||
* are considered optional: for example <code>capital of Hungary</code> is equal to
|
||||
* <code>capital OR of OR Hungary</code>.<br/>
|
||||
* In <code>DEFAULT_OPERATOR_AND</code> terms are considered to be in conjuction: the
|
||||
* above mentioned query is parsed as <code>capital AND of AND Hungary</code>
|
||||
*/
|
||||
public void setOperator(int operator) {
|
||||
this.operator = operator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets implicit operator setting, which will be either DEFAULT_OPERATOR_AND
|
||||
* or DEFAULT_OPERATOR_OR.
|
||||
*/
|
||||
public int getOperator() {
|
||||
return operator;
|
||||
}
|
||||
|
||||
public void setLowercaseWildcardTerms(boolean lowercaseWildcardTerms) {
|
||||
this.lowercaseWildcardTerms = lowercaseWildcardTerms;
|
||||
}
|
||||
|
||||
public boolean getLowercaseWildcardTerms() {
|
||||
return lowercaseWildcardTerms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set locale used by date range parsing.
|
||||
*/
|
||||
public void setLocale(Locale locale) {
|
||||
this.locale = locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current locale, allowing access by subclasses.
|
||||
*/
|
||||
public Locale getLocale() {
|
||||
return locale;
|
||||
}
|
||||
|
||||
protected void addClause(Vector clauses, int conj, int mods, Query q) {
|
||||
boolean required, prohibited;
|
||||
|
||||
// If this term is introduced by AND, make the preceding term required,
|
||||
// unless it's already prohibited
|
||||
if (clauses.size() > 0 && conj == CONJ_AND) {
|
||||
BooleanClause c = (BooleanClause) clauses.elementAt(clauses.size()-1);
|
||||
if (!c.prohibited)
|
||||
c.required = true;
|
||||
}
|
||||
|
||||
if (clauses.size() > 0 && operator == DEFAULT_OPERATOR_AND && conj == CONJ_OR) {
|
||||
// If this term is introduced by OR, make the preceding term optional,
|
||||
// unless it's prohibited (that means we leave -a OR b but +a OR b-->a OR b)
|
||||
// notice if the input is a OR b, first term is parsed as required; without
|
||||
// this modification a OR b would parsed as +a OR b
|
||||
BooleanClause c = (BooleanClause) clauses.elementAt(clauses.size()-1);
|
||||
if (!c.prohibited)
|
||||
c.required = false;
|
||||
}
|
||||
|
||||
// We might have been passed a null query; the term might have been
|
||||
// filtered away by the analyzer.
|
||||
if (q == null)
|
||||
return;
|
||||
|
||||
if (operator == DEFAULT_OPERATOR_OR) {
|
||||
// We set REQUIRED if we're introduced by AND or +; PROHIBITED if
|
||||
// introduced by NOT or -; make sure not to set both.
|
||||
prohibited = (mods == MOD_NOT);
|
||||
required = (mods == MOD_REQ);
|
||||
if (conj == CONJ_AND && !prohibited) {
|
||||
required = true;
|
||||
}
|
||||
} else {
|
||||
// We set PROHIBITED if we're introduced by NOT or -; We set REQUIRED
|
||||
// if not PROHIBITED and not introduced by OR
|
||||
prohibited = (mods == MOD_NOT);
|
||||
required = (!prohibited && conj != CONJ_OR);
|
||||
}
|
||||
clauses.addElement(new BooleanClause(q, required, prohibited));
|
||||
}
|
||||
|
||||
/**
|
||||
* Note that parameter analyzer is ignored. Calls inside the parser always
|
||||
* use class member analyser. This method will be deprecated and substituted
|
||||
* by {@link #getFieldQuery(String, String)} in future versions of Lucene.
|
||||
* Currently overwriting either of these methods works.
|
||||
*
|
||||
* @exception ParseException throw in overridden method to disallow
|
||||
*/
|
||||
protected Query getFieldQuery(String field,
|
||||
Analyzer analyzer,
|
||||
String queryText) throws ParseException {
|
||||
return getFieldQuery(field, queryText);
|
||||
}
|
||||
|
||||
/**
|
||||
* @exception ParseException throw in overridden method to disallow
|
||||
*/
|
||||
protected Query getFieldQuery(String field, String queryText) throws ParseException {
|
||||
// Use the analyzer to get all the tokens, and then build a TermQuery,
|
||||
// PhraseQuery, or nothing based on the term count
|
||||
|
||||
TokenStream source = analyzer.tokenStream(field,
|
||||
new StringReader(queryText));
|
||||
Vector v = new Vector();
|
||||
org.apache.lucene.analysis.Token t;
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
t = source.next();
|
||||
}
|
||||
catch (IOException e) {
|
||||
t = null;
|
||||
}
|
||||
if (t == null)
|
||||
break;
|
||||
v.addElement(t.termText());
|
||||
}
|
||||
try {
|
||||
source.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
if (v.size() == 0)
|
||||
return null;
|
||||
else if (v.size() == 1)
|
||||
return new TermQuery(new Term(field, (String) v.elementAt(0)));
|
||||
else {
|
||||
PhraseQuery q = new PhraseQuery();
|
||||
q.setSlop(phraseSlop);
|
||||
for (int i=0; i<v.size(); i++) {
|
||||
q.add(new Term(field, (String) v.elementAt(i)));
|
||||
}
|
||||
return q;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base implementation delegates to {@link #getFieldQuery(String, Analyzer, String)}.
|
||||
* This method may be overwritten, for example, to return
|
||||
* a SpanNearQuery instead of a PhraseQuery.
|
||||
*
|
||||
* Note that parameter analyzer is ignored. Calls inside the parser always
|
||||
* use class member analyser. This method will be deprecated and substituted
|
||||
* by {@link #getFieldQuery(String, String, int)} in future versions of Lucene.
|
||||
* Currently overwriting either of these methods works.
|
||||
*
|
||||
* @exception ParseException throw in overridden method to disallow
|
||||
*/
|
||||
protected Query getFieldQuery(String field,
|
||||
Analyzer analyzer,
|
||||
String queryText,
|
||||
int slop) throws ParseException {
|
||||
return getFieldQuery(field, queryText, slop);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base implementation delegates to {@link #getFieldQuery(String,String)}.
|
||||
* This method may be overridden, for example, to return
|
||||
* a SpanNearQuery instead of a PhraseQuery.
|
||||
*
|
||||
* @exception ParseException throw in overridden method to disallow
|
||||
*/
|
||||
protected Query getFieldQuery(String field, String queryText, int slop)
|
||||
throws ParseException {
|
||||
Query query = getFieldQuery(field, queryText);
|
||||
|
||||
if (query instanceof PhraseQuery) {
|
||||
((PhraseQuery) query).setSlop(slop);
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note that parameter analyzer is ignored. Calls inside the parser always
|
||||
* use class member analyser. This method will be deprecated and substituted
|
||||
* by {@link #getRangeQuery(String, String, String, boolean)} in future versions of Lucene.
|
||||
* Currently overwriting either of these methods works.
|
||||
*
|
||||
* @exception ParseException throw in overridden method to disallow
|
||||
*/
|
||||
protected Query getRangeQuery(String field,
|
||||
Analyzer analyzer,
|
||||
String part1,
|
||||
String part2,
|
||||
boolean inclusive) throws ParseException {
|
||||
return getRangeQuery(field, part1, part2, inclusive);
|
||||
}
|
||||
|
||||
/**
|
||||
* @exception ParseException throw in overridden method to disallow
|
||||
*/
|
||||
protected Query getRangeQuery(String field,
|
||||
String part1,
|
||||
String part2,
|
||||
boolean inclusive) throws ParseException
|
||||
{
|
||||
try {
|
||||
DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
|
||||
df.setLenient(true);
|
||||
Date d1 = df.parse(part1);
|
||||
Date d2 = df.parse(part2);
|
||||
part1 = DateField.dateToString(d1);
|
||||
part2 = DateField.dateToString(d2);
|
||||
}
|
||||
catch (Exception e) { }
|
||||
|
||||
return new RangeQuery(new Term(field, part1),
|
||||
new Term(field, part2),
|
||||
inclusive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method for generating query, given a set of clauses.
|
||||
* By default creates a boolean query composed of clauses passed in.
|
||||
*
|
||||
* Can be overridden by extending classes, to modify query being
|
||||
* returned.
|
||||
*
|
||||
* @param clauses Vector that contains {@link BooleanClause} instances
|
||||
* to join.
|
||||
*
|
||||
* @return Resulting {@link Query} object.
|
||||
* @exception ParseException throw in overridden method to disallow
|
||||
*/
|
||||
protected Query getBooleanQuery(Vector clauses) throws ParseException
|
||||
{
|
||||
BooleanQuery query = new BooleanQuery();
|
||||
query.
|
||||
for (int i = 0; i < clauses.size(); i++) {
|
||||
query.add((BooleanClause)clauses.elementAt(i));
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method for generating a query. Called when parser
|
||||
* parses an input term token that contains one or more wildcard
|
||||
* characters (? and *), but is not a prefix term token (one
|
||||
* that has just a single * character at the end)
|
||||
*<p>
|
||||
* Depending on settings, prefix term may be lower-cased
|
||||
* automatically. It will not go through the default Analyzer,
|
||||
* however, since normal Analyzers are unlikely to work properly
|
||||
* with wildcard templates.
|
||||
*<p>
|
||||
* Can be overridden by extending classes, to provide custom handling for
|
||||
* wildcard queries, which may be necessary due to missing analyzer calls.
|
||||
*
|
||||
* @param field Name of the field query will use.
|
||||
* @param termStr Term token that contains one or more wild card
|
||||
* characters (? or *), but is not simple prefix term
|
||||
*
|
||||
* @return Resulting {@link Query} built for the term
|
||||
* @exception ParseException throw in overridden method to disallow
|
||||
*/
|
||||
protected Query getWildcardQuery(String field, String termStr) throws ParseException
|
||||
{
|
||||
if (lowercaseWildcardTerms) {
|
||||
termStr = termStr.toLowerCase();
|
||||
}
|
||||
Term t = new Term(field, termStr);
|
||||
return new WildcardQuery(t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method for generating a query (similar to
|
||||
* ({@link #getWildcardQuery}). Called when parser parses an input term
|
||||
* token that uses prefix notation; that is, contains a single '*' wildcard
|
||||
* character as its last character. Since this is a special case
|
||||
* of generic wildcard term, and such a query can be optimized easily,
|
||||
* this usually results in a different query object.
|
||||
*<p>
|
||||
* Depending on settings, a prefix term may be lower-cased
|
||||
* automatically. It will not go through the default Analyzer,
|
||||
* however, since normal Analyzers are unlikely to work properly
|
||||
* with wildcard templates.
|
||||
*<p>
|
||||
* Can be overridden by extending classes, to provide custom handling for
|
||||
* wild card queries, which may be necessary due to missing analyzer calls.
|
||||
*
|
||||
* @param field Name of the field query will use.
|
||||
* @param termStr Term token to use for building term for the query
|
||||
* (<b>without</b> trailing '*' character!)
|
||||
*
|
||||
* @return Resulting {@link Query} built for the term
|
||||
* @exception ParseException throw in overridden method to disallow
|
||||
*/
|
||||
protected Query getPrefixQuery(String field, String termStr) throws ParseException
|
||||
{
|
||||
if (lowercaseWildcardTerms) {
|
||||
termStr = termStr.toLowerCase();
|
||||
}
|
||||
Term t = new Term(field, termStr);
|
||||
return new PrefixQuery(t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method for generating a query (similar to
|
||||
* ({@link #getWildcardQuery}). Called when parser parses
|
||||
* an input term token that has the fuzzy suffix (~) appended.
|
||||
*
|
||||
* @param field Name of the field query will use.
|
||||
* @param termStr Term token to use for building term for the query
|
||||
*
|
||||
* @return Resulting {@link Query} built for the term
|
||||
* @exception ParseException throw in overridden method to disallow
|
||||
*/
|
||||
protected Query getFuzzyQuery(String field, String termStr) throws ParseException {
|
||||
return getFuzzyQuery(field, termStr, fuzzyMinSim);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method for generating a query (similar to
|
||||
* ({@link #getWildcardQuery}). Called when parser parses
|
||||
* an input term token that has the fuzzy suffix (~floatNumber) appended.
|
||||
*
|
||||
* @param field Name of the field query will use.
|
||||
* @param termStr Term token to use for building term for the query
|
||||
* @param minSimilarity the minimum similarity required for a fuzzy match
|
||||
*
|
||||
* @return Resulting {@link Query} built for the term
|
||||
* @exception ParseException throw in overridden method to disallow
|
||||
*/
|
||||
protected Query getFuzzyQuery(String field, String termStr, float minSimilarity) throws ParseException
|
||||
{
|
||||
Term t = new Term(field, termStr);
|
||||
return new FuzzyQuery(t, minSimilarity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a String where the escape char has been
|
||||
* removed, or kept only once if there was a double escape.
|
||||
*/
|
||||
private String discardEscapeChar(String input) {
|
||||
char[] caSource = input.toCharArray();
|
||||
char[] caDest = new char[caSource.length];
|
||||
int j = 0;
|
||||
for (int i = 0; i < caSource.length; i++) {
|
||||
if ((caSource[i] != '\\') || (i > 0 && caSource[i-1] == '\\')) {
|
||||
caDest[j++]=caSource[i];
|
||||
}
|
||||
}
|
||||
return new String(caDest, 0, j);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a String where those characters that QueryParser
|
||||
* expects to be escaped are escaped, i.e. preceded by a <code>\</code>.
|
||||
*/
|
||||
public static String escape(String s) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char c = s.charAt(i);
|
||||
// NOTE: keep this in sync with _ESCAPED_CHAR below!
|
||||
if (c == '\\' || c == '+' || c == '-' || c == '!' || c == '(' || c == ')' || c == ':'
|
||||
|| c == '^' || c == '[' || c == ']' || c == '\"' || c == '{' || c == '}' || c == '~'
|
||||
|| c == '*' || c == '?') {
|
||||
sb.append('\\');
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
QueryParser qp = new QueryParser("field",
|
||||
new org.apache.lucene.analysis.SimpleAnalyzer());
|
||||
Query q = qp.parse(args[0]);
|
||||
System.out.println(q.toString("field"));
|
||||
}
|
||||
}
|
||||
|
||||
PARSER_END(QueryParser)
|
||||
|
||||
/* ***************** */
|
||||
/* Token Definitions */
|
||||
/* ***************** */
|
||||
|
||||
<*> TOKEN : {
|
||||
<#_NUM_CHAR: ["0"-"9"] >
|
||||
// NOTE: keep this in sync with escape(String) above!
|
||||
| <#_ESCAPED_CHAR: "\\" [ "\\", "+", "-", "!", "(", ")", ":", "^",
|
||||
"[", "]", "\"", "{", "}", "~", "*", "?" ] >
|
||||
| <#_TERM_START_CHAR: ( ~[ " ", "\t", "\n", "\r", "+", "-", "!", "(", ")", ":", "^",
|
||||
"[", "]", "\"", "{", "}", "~", "*", "?" ]
|
||||
| <_ESCAPED_CHAR> ) >
|
||||
| <#_TERM_CHAR: ( <_TERM_START_CHAR> | <_ESCAPED_CHAR> | "-" | "+" ) >
|
||||
| <#_WHITESPACE: ( " " | "\t" | "\n" | "\r") >
|
||||
}
|
||||
|
||||
<DEFAULT, RangeIn, RangeEx> SKIP : {
|
||||
<<_WHITESPACE>>
|
||||
}
|
||||
|
||||
// OG: to support prefix queries:
|
||||
// http://nagoya.apache.org/bugzilla/show_bug.cgi?id=12137
|
||||
// Change from:
|
||||
// | <WILDTERM: <_TERM_START_CHAR>
|
||||
// (<_TERM_CHAR> | ( [ "*", "?" ] ))* >
|
||||
// To:
|
||||
//
|
||||
// | <WILDTERM: (<_TERM_CHAR> | ( [ "*", "?" ] ))* >
|
||||
|
||||
<DEFAULT> TOKEN : {
|
||||
<AND: ("AND" | "&&") >
|
||||
| <OR: ("OR" | "||") >
|
||||
| <NOT: ("NOT" | "!") >
|
||||
| <PLUS: "+" >
|
||||
| <MINUS: "-" >
|
||||
| <LPAREN: "(" >
|
||||
| <RPAREN: ")" >
|
||||
| <COLON: ":" >
|
||||
| <CARAT: "^" > : Boost
|
||||
| <QUOTED: "\"" (~["\""])+ "\"">
|
||||
| <TERM: <_TERM_START_CHAR> (<_TERM_CHAR>)* >
|
||||
| <FUZZY_SLOP: "~" ( (<_NUM_CHAR>)+ ( "." (<_NUM_CHAR>)+ )? )? >
|
||||
| <PREFIXTERM: <_TERM_START_CHAR> (<_TERM_CHAR>)* "*" >
|
||||
| <WILDTERM: (<_TERM_START_CHAR> | ( [ "*", "?" ] ))
|
||||
(<_TERM_CHAR> | ( [ "*", "?" ] ))* >
|
||||
| <RANGEIN_START: "[" > : RangeIn
|
||||
| <RANGEEX_START: "{" > : RangeEx
|
||||
}
|
||||
|
||||
<Boost> TOKEN : {
|
||||
<NUMBER: (<_NUM_CHAR>)+ ( "." (<_NUM_CHAR>)+ )? > : DEFAULT
|
||||
}
|
||||
|
||||
<RangeIn> TOKEN : {
|
||||
<RANGEIN_TO: "TO">
|
||||
| <RANGEIN_END: "]"> : DEFAULT
|
||||
| <RANGEIN_QUOTED: "\"" (~["\""])+ "\"">
|
||||
| <RANGEIN_GOOP: (~[ " ", "]" ])+ >
|
||||
}
|
||||
|
||||
<RangeEx> TOKEN : {
|
||||
<RANGEEX_TO: "TO">
|
||||
| <RANGEEX_END: "}"> : DEFAULT
|
||||
| <RANGEEX_QUOTED: "\"" (~["\""])+ "\"">
|
||||
| <RANGEEX_GOOP: (~[ " ", "}" ])+ >
|
||||
}
|
||||
|
||||
// * Query ::= ( Clause )*
|
||||
// * Clause ::= ["+", "-"] [<TERM> ":"] ( <TERM> | "(" Query ")" )
|
||||
|
||||
int Conjunction() : {
|
||||
int ret = CONJ_NONE;
|
||||
}
|
||||
{
|
||||
[
|
||||
<AND> { ret = CONJ_AND; }
|
||||
| <OR> { ret = CONJ_OR; }
|
||||
]
|
||||
{ return ret; }
|
||||
}
|
||||
|
||||
int Modifiers() : {
|
||||
int ret = MOD_NONE;
|
||||
}
|
||||
{
|
||||
[
|
||||
<PLUS> { ret = MOD_REQ; }
|
||||
| <MINUS> { ret = MOD_NOT; }
|
||||
| <NOT> { ret = MOD_NOT; }
|
||||
]
|
||||
{ return ret; }
|
||||
}
|
||||
|
||||
Query Query(String field) :
|
||||
{
|
||||
Vector clauses = new Vector();
|
||||
Query q, firstQuery=null;
|
||||
int conj, mods;
|
||||
}
|
||||
{
|
||||
mods=Modifiers() q=Clause(field)
|
||||
{
|
||||
addClause(clauses, CONJ_NONE, mods, q);
|
||||
if (mods == MOD_NONE)
|
||||
firstQuery=q;
|
||||
}
|
||||
(
|
||||
conj=Conjunction() mods=Modifiers() q=Clause(field)
|
||||
{ addClause(clauses, conj, mods, q); }
|
||||
)*
|
||||
{
|
||||
if (clauses.size() == 1 && firstQuery != null)
|
||||
return firstQuery;
|
||||
else {
|
||||
return getBooleanQuery(clauses);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Query Clause(String field) : {
|
||||
Query q;
|
||||
Token fieldToken=null, boost=null;
|
||||
}
|
||||
{
|
||||
[
|
||||
LOOKAHEAD(2)
|
||||
fieldToken=<TERM> <COLON> {
|
||||
field=discardEscapeChar(fieldToken.image);
|
||||
}
|
||||
]
|
||||
|
||||
(
|
||||
q=Term(field)
|
||||
| <LPAREN> q=Query(field) <RPAREN> (<CARAT> boost=<NUMBER>)?
|
||||
|
||||
)
|
||||
{
|
||||
if (boost != null) {
|
||||
float f = (float)1.0;
|
||||
try {
|
||||
f = Float.valueOf(boost.image).floatValue();
|
||||
q.setBoost(f);
|
||||
} catch (Exception ignored) { }
|
||||
}
|
||||
return q;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Query Term(String field) : {
|
||||
Token term, boost=null, fuzzySlop=null, goop1, goop2;
|
||||
boolean prefix = false;
|
||||
boolean wildcard = false;
|
||||
boolean fuzzy = false;
|
||||
boolean rangein = false;
|
||||
Query q;
|
||||
}
|
||||
{
|
||||
(
|
||||
(
|
||||
term=<TERM>
|
||||
| term=<PREFIXTERM> { prefix=true; }
|
||||
| term=<WILDTERM> { wildcard=true; }
|
||||
| term=<NUMBER>
|
||||
)
|
||||
[ fuzzySlop=<FUZZY_SLOP> { fuzzy=true; } ]
|
||||
[ <CARAT> boost=<NUMBER> [ fuzzySlop=<FUZZY_SLOP> { fuzzy=true; } ] ]
|
||||
{
|
||||
String termImage=discardEscapeChar(term.image);
|
||||
if (wildcard) {
|
||||
q = getWildcardQuery(field, termImage);
|
||||
} else if (prefix) {
|
||||
q = getPrefixQuery(field,
|
||||
discardEscapeChar(term.image.substring
|
||||
(0, term.image.length()-1)));
|
||||
} else if (fuzzy) {
|
||||
float fms = fuzzyMinSim;
|
||||
try {
|
||||
fms = Float.valueOf(fuzzySlop.image.substring(1)).floatValue();
|
||||
} catch (Exception ignored) { }
|
||||
if(fms < 0.0f || fms > 1.0f){
|
||||
throw new ParseException("Minimum similarity for a FuzzyQuery has to be between 0.0f and 1.0f !");
|
||||
}
|
||||
if(fms == fuzzyMinSim)
|
||||
q = getFuzzyQuery(field, termImage);
|
||||
else
|
||||
q = getFuzzyQuery(field, termImage, fms);
|
||||
} else {
|
||||
q = getFieldQuery(field, analyzer, termImage);
|
||||
}
|
||||
}
|
||||
| ( <RANGEIN_START> ( goop1=<RANGEIN_GOOP>|goop1=<RANGEIN_QUOTED> )
|
||||
[ <RANGEIN_TO> ] ( goop2=<RANGEIN_GOOP>|goop2=<RANGEIN_QUOTED> )
|
||||
<RANGEIN_END> )
|
||||
[ <CARAT> boost=<NUMBER> ]
|
||||
{
|
||||
if (goop1.kind == RANGEIN_QUOTED) {
|
||||
goop1.image = goop1.image.substring(1, goop1.image.length()-1);
|
||||
} else {
|
||||
goop1.image = discardEscapeChar(goop1.image);
|
||||
}
|
||||
if (goop2.kind == RANGEIN_QUOTED) {
|
||||
goop2.image = goop2.image.substring(1, goop2.image.length()-1);
|
||||
} else {
|
||||
goop2.image = discardEscapeChar(goop2.image);
|
||||
}
|
||||
q = getRangeQuery(field, analyzer, goop1.image, goop2.image, true);
|
||||
}
|
||||
| ( <RANGEEX_START> ( goop1=<RANGEEX_GOOP>|goop1=<RANGEEX_QUOTED> )
|
||||
[ <RANGEEX_TO> ] ( goop2=<RANGEEX_GOOP>|goop2=<RANGEEX_QUOTED> )
|
||||
<RANGEEX_END> )
|
||||
[ <CARAT> boost=<NUMBER> ]
|
||||
{
|
||||
if (goop1.kind == RANGEEX_QUOTED) {
|
||||
goop1.image = goop1.image.substring(1, goop1.image.length()-1);
|
||||
} else {
|
||||
goop1.image = discardEscapeChar(goop1.image);
|
||||
}
|
||||
if (goop2.kind == RANGEEX_QUOTED) {
|
||||
goop2.image = goop2.image.substring(1, goop2.image.length()-1);
|
||||
} else {
|
||||
goop2.image = discardEscapeChar(goop2.image);
|
||||
}
|
||||
|
||||
q = getRangeQuery(field, analyzer, goop1.image, goop2.image, false);
|
||||
}
|
||||
| term=<QUOTED>
|
||||
[ fuzzySlop=<FUZZY_SLOP> ]
|
||||
[ <CARAT> boost=<NUMBER> ]
|
||||
{
|
||||
int s = phraseSlop;
|
||||
|
||||
if (fuzzySlop != null) {
|
||||
try {
|
||||
s = Float.valueOf(fuzzySlop.image.substring(1)).intValue();
|
||||
}
|
||||
catch (Exception ignored) { }
|
||||
}
|
||||
q = getFieldQuery(field, analyzer, term.image.substring(1, term.image.length()-1), s);
|
||||
}
|
||||
)
|
||||
{
|
||||
if (boost != null) {
|
||||
float f = (float) 1.0;
|
||||
try {
|
||||
f = Float.valueOf(boost.image).floatValue();
|
||||
}
|
||||
catch (Exception ignored) {
|
||||
/* Should this be handled somehow? (defaults to "no boost", if
|
||||
* boost number is invalid)
|
||||
*/
|
||||
}
|
||||
|
||||
// avoid boosting null queries, such as those caused by stop words
|
||||
if (q != null) {
|
||||
q.setBoost(f);
|
||||
}
|
||||
}
|
||||
return q;
|
||||
}
|
||||
}
|
@@ -0,0 +1,78 @@
|
||||
/* Generated By:JavaCC: Do not edit this line. QueryParserConstants.java */
|
||||
package org.alfresco.repo.search.impl.lucene;
|
||||
|
||||
public interface QueryParserConstants {
|
||||
|
||||
int EOF = 0;
|
||||
int _NUM_CHAR = 1;
|
||||
int _ESCAPED_CHAR = 2;
|
||||
int _TERM_START_CHAR = 3;
|
||||
int _TERM_CHAR = 4;
|
||||
int _WHITESPACE = 5;
|
||||
int AND = 7;
|
||||
int OR = 8;
|
||||
int NOT = 9;
|
||||
int PLUS = 10;
|
||||
int MINUS = 11;
|
||||
int LPAREN = 12;
|
||||
int RPAREN = 13;
|
||||
int COLON = 14;
|
||||
int CARAT = 15;
|
||||
int QUOTED = 16;
|
||||
int TERM = 17;
|
||||
int FUZZY_SLOP = 18;
|
||||
int PREFIXTERM = 19;
|
||||
int WILDTERM = 20;
|
||||
int RANGEIN_START = 21;
|
||||
int RANGEEX_START = 22;
|
||||
int NUMBER = 23;
|
||||
int RANGEIN_TO = 24;
|
||||
int RANGEIN_END = 25;
|
||||
int RANGEIN_QUOTED = 26;
|
||||
int RANGEIN_GOOP = 27;
|
||||
int RANGEEX_TO = 28;
|
||||
int RANGEEX_END = 29;
|
||||
int RANGEEX_QUOTED = 30;
|
||||
int RANGEEX_GOOP = 31;
|
||||
|
||||
int Boost = 0;
|
||||
int RangeEx = 1;
|
||||
int RangeIn = 2;
|
||||
int DEFAULT = 3;
|
||||
|
||||
String[] tokenImage = {
|
||||
"<EOF>",
|
||||
"<_NUM_CHAR>",
|
||||
"<_ESCAPED_CHAR>",
|
||||
"<_TERM_START_CHAR>",
|
||||
"<_TERM_CHAR>",
|
||||
"<_WHITESPACE>",
|
||||
"<token of kind 6>",
|
||||
"<AND>",
|
||||
"<OR>",
|
||||
"<NOT>",
|
||||
"\"+\"",
|
||||
"\"-\"",
|
||||
"\"(\"",
|
||||
"\")\"",
|
||||
"\":\"",
|
||||
"\"^\"",
|
||||
"<QUOTED>",
|
||||
"<TERM>",
|
||||
"<FUZZY_SLOP>",
|
||||
"<PREFIXTERM>",
|
||||
"<WILDTERM>",
|
||||
"\"[\"",
|
||||
"\"{\"",
|
||||
"<NUMBER>",
|
||||
"\"TO\"",
|
||||
"\"]\"",
|
||||
"<RANGEIN_QUOTED>",
|
||||
"<RANGEIN_GOOP>",
|
||||
"\"TO\"",
|
||||
"\"}\"",
|
||||
"<RANGEEX_QUOTED>",
|
||||
"<RANGEEX_GOOP>",
|
||||
};
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
97
source/java/org/alfresco/repo/search/impl/lucene/Token.java
Normal file
97
source/java/org/alfresco/repo/search/impl/lucene/Token.java
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/* Generated By:JavaCC: Do not edit this line. Token.java Version 3.0 */
|
||||
package org.alfresco.repo.search.impl.lucene;
|
||||
|
||||
/**
|
||||
* Describes the input token stream.
|
||||
*/
|
||||
|
||||
public class Token {
|
||||
|
||||
/**
|
||||
* An integer that describes the kind of this token. This numbering
|
||||
* system is determined by JavaCCParser, and a table of these numbers is
|
||||
* stored in the file ...Constants.java.
|
||||
*/
|
||||
public int kind;
|
||||
|
||||
/**
|
||||
* beginLine and beginColumn describe the position of the first character
|
||||
* of this token; endLine and endColumn describe the position of the
|
||||
* last character of this token.
|
||||
*/
|
||||
public int beginLine, beginColumn, endLine, endColumn;
|
||||
|
||||
/**
|
||||
* The string image of the token.
|
||||
*/
|
||||
public String image;
|
||||
|
||||
/**
|
||||
* A reference to the next regular (non-special) token from the input
|
||||
* stream. If this is the last token from the input stream, or if the
|
||||
* token manager has not read tokens beyond this one, this field is
|
||||
* set to null. This is true only if this token is also a regular
|
||||
* token. Otherwise, see below for a description of the contents of
|
||||
* this field.
|
||||
*/
|
||||
public Token next;
|
||||
|
||||
/**
|
||||
* This field is used to access special tokens that occur prior to this
|
||||
* token, but after the immediately preceding regular (non-special) token.
|
||||
* If there are no such special tokens, this field is set to null.
|
||||
* When there are more than one such special token, this field refers
|
||||
* to the last of these special tokens, which in turn refers to the next
|
||||
* previous special token through its specialToken field, and so on
|
||||
* until the first special token (whose specialToken field is null).
|
||||
* The next fields of special tokens refer to other special tokens that
|
||||
* immediately follow it (without an intervening regular token). If there
|
||||
* is no such token, this field is null.
|
||||
*/
|
||||
public Token specialToken;
|
||||
|
||||
/**
|
||||
* Returns the image.
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new Token object, by default. However, if you want, you
|
||||
* can create and return subclass objects based on the value of ofKind.
|
||||
* Simply add the cases to the switch for all those special cases.
|
||||
* For example, if you have a subclass of Token called IDToken that
|
||||
* you want to create if ofKind is ID, simlpy add something like :
|
||||
*
|
||||
* case MyParserConstants.ID : return new IDToken();
|
||||
*
|
||||
* to the following switch statement. Then you can cast matchedToken
|
||||
* variable to the appropriate type and use it in your lexical actions.
|
||||
*/
|
||||
public static final Token newToken(int ofKind)
|
||||
{
|
||||
switch(ofKind)
|
||||
{
|
||||
default : return new Token();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,133 @@
|
||||
/* Generated By:JavaCC: Do not edit this line. TokenMgrError.java Version 3.0 */
|
||||
package org.alfresco.repo.search.impl.lucene;
|
||||
|
||||
public class TokenMgrError extends Error
|
||||
{
|
||||
/*
|
||||
* Ordinals for various reasons why an Error of this type can be thrown.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Lexical error occured.
|
||||
*/
|
||||
static final int LEXICAL_ERROR = 0;
|
||||
|
||||
/**
|
||||
* An attempt wass made to create a second instance of a static token manager.
|
||||
*/
|
||||
static final int STATIC_LEXER_ERROR = 1;
|
||||
|
||||
/**
|
||||
* Tried to change to an invalid lexical state.
|
||||
*/
|
||||
static final int INVALID_LEXICAL_STATE = 2;
|
||||
|
||||
/**
|
||||
* Detected (and bailed out of) an infinite loop in the token manager.
|
||||
*/
|
||||
static final int LOOP_DETECTED = 3;
|
||||
|
||||
/**
|
||||
* Indicates the reason why the exception is thrown. It will have
|
||||
* one of the above 4 values.
|
||||
*/
|
||||
int errorCode;
|
||||
|
||||
/**
|
||||
* Replaces unprintable characters by their espaced (or unicode escaped)
|
||||
* equivalents in the given string
|
||||
*/
|
||||
protected static final String addEscapes(String str) {
|
||||
StringBuilder retval = new StringBuilder(str.length() + 8);
|
||||
char ch;
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
switch (str.charAt(i))
|
||||
{
|
||||
case 0 :
|
||||
continue;
|
||||
case '\b':
|
||||
retval.append("\\b");
|
||||
continue;
|
||||
case '\t':
|
||||
retval.append("\\t");
|
||||
continue;
|
||||
case '\n':
|
||||
retval.append("\\n");
|
||||
continue;
|
||||
case '\f':
|
||||
retval.append("\\f");
|
||||
continue;
|
||||
case '\r':
|
||||
retval.append("\\r");
|
||||
continue;
|
||||
case '\"':
|
||||
retval.append("\\\"");
|
||||
continue;
|
||||
case '\'':
|
||||
retval.append("\\\'");
|
||||
continue;
|
||||
case '\\':
|
||||
retval.append("\\\\");
|
||||
continue;
|
||||
default:
|
||||
if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
|
||||
String s = "0000" + Integer.toString(ch, 16);
|
||||
retval.append("\\u" + s.substring(s.length() - 4, s.length()));
|
||||
} else {
|
||||
retval.append(ch);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return retval.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a detailed message for the Error when it is thrown by the
|
||||
* token manager to indicate a lexical error.
|
||||
* Parameters :
|
||||
* EOFSeen : indicates if EOF caused the lexicl error
|
||||
* curLexState : lexical state in which this error occured
|
||||
* errorLine : line number when the error occured
|
||||
* errorColumn : column number when the error occured
|
||||
* errorAfter : prefix that was seen before this error occured
|
||||
* curchar : the offending character
|
||||
* Note: You can customize the lexical error message by modifying this method.
|
||||
*/
|
||||
protected static String LexicalError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar) {
|
||||
return("Lexical error at line " +
|
||||
errorLine + ", column " +
|
||||
errorColumn + ". Encountered: " +
|
||||
(EOFSeen ? "<EOF> " : ("\"" + addEscapes(String.valueOf(curChar)) + "\"") + " (" + (int)curChar + "), ") +
|
||||
"after : \"" + addEscapes(errorAfter) + "\"");
|
||||
}
|
||||
|
||||
/**
|
||||
* You can also modify the body of this method to customize your error messages.
|
||||
* For example, cases like LOOP_DETECTED and INVALID_LEXICAL_STATE are not
|
||||
* of end-users concern, so you can return something like :
|
||||
*
|
||||
* "Internal Error : Please file a bug report .... "
|
||||
*
|
||||
* from this method for such cases in the release version of your parser.
|
||||
*/
|
||||
public String getMessage() {
|
||||
return super.getMessage();
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructors of various flavors follow.
|
||||
*/
|
||||
|
||||
public TokenMgrError() {
|
||||
}
|
||||
|
||||
public TokenMgrError(String message, int reason) {
|
||||
super(message);
|
||||
errorCode = reason;
|
||||
}
|
||||
|
||||
public TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar, int reason) {
|
||||
this(LexicalError(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason);
|
||||
}
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.analysis;
|
||||
|
||||
import java.io.Reader;
|
||||
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.analysis.TokenStream;
|
||||
|
||||
/**
|
||||
* @author andyh
|
||||
*
|
||||
* TODO To change the template for this generated type comment go to Window -
|
||||
* Preferences - Java - Code Style - Code Templates
|
||||
*/
|
||||
public class CategoryAnalyser extends Analyzer
|
||||
{
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.apache.lucene.analysis.Analyzer#tokenStream(java.lang.String,
|
||||
* java.io.Reader)
|
||||
*/
|
||||
public TokenStream tokenStream(String fieldName, Reader reader)
|
||||
{
|
||||
return new PathTokenFilter(reader, PathTokenFilter.PATH_SEPARATOR,
|
||||
PathTokenFilter.SEPARATOR_TOKEN_TEXT, PathTokenFilter.NO_NS_TOKEN_TEXT,
|
||||
PathTokenFilter.NAMESPACE_START_DELIMITER, PathTokenFilter.NAMESPACE_END_DELIMITER, false);
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.analysis;
|
||||
|
||||
import java.io.Reader;
|
||||
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.analysis.TokenStream;
|
||||
|
||||
public class DateAnalyser extends Analyzer
|
||||
{
|
||||
|
||||
public DateAnalyser()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
// Split at the T in the XML date form
|
||||
public TokenStream tokenStream(String fieldName, Reader reader)
|
||||
{
|
||||
return new DateTokenFilter(reader);
|
||||
}
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.analysis;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import org.alfresco.util.CachingDateFormat;
|
||||
import org.apache.lucene.analysis.Token;
|
||||
import org.apache.lucene.analysis.Tokenizer;
|
||||
import org.apache.lucene.analysis.WhitespaceTokenizer;
|
||||
|
||||
/**
|
||||
* @author andyh
|
||||
*/
|
||||
public class DateTokenFilter extends Tokenizer
|
||||
{
|
||||
Tokenizer baseTokeniser;
|
||||
|
||||
public DateTokenFilter(Reader in)
|
||||
{
|
||||
super(in);
|
||||
baseTokeniser = new WhitespaceTokenizer(in);
|
||||
}
|
||||
|
||||
public Token next() throws IOException
|
||||
{
|
||||
SimpleDateFormat df = CachingDateFormat.getDateFormat();
|
||||
SimpleDateFormat dof = CachingDateFormat.getDateOnlyFormat();
|
||||
Token candidate;
|
||||
while((candidate = baseTokeniser.next()) != null)
|
||||
{
|
||||
Date date;
|
||||
try
|
||||
{
|
||||
date = df.parse(candidate.termText());
|
||||
}
|
||||
catch (ParseException e)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
String valueString = dof.format(date);
|
||||
Token integerToken = new Token(valueString, candidate.startOffset(), candidate.startOffset(),
|
||||
candidate.type());
|
||||
return integerToken;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.analysis;
|
||||
|
||||
/**
|
||||
* Simple analyser to wrap the tokenisation of doubles.
|
||||
*
|
||||
* @author Andy Hind
|
||||
*/
|
||||
import java.io.Reader;
|
||||
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.analysis.TokenStream;
|
||||
|
||||
public class DoubleAnalyser extends Analyzer
|
||||
{
|
||||
|
||||
public DoubleAnalyser()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
|
||||
public TokenStream tokenStream(String fieldName, Reader reader)
|
||||
{
|
||||
return new DoubleTokenFilter(reader);
|
||||
}
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.analysis;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
|
||||
import org.apache.lucene.analysis.Token;
|
||||
import org.apache.lucene.analysis.Tokenizer;
|
||||
import org.apache.lucene.analysis.standard.StandardTokenizer;
|
||||
|
||||
/**
|
||||
* Simple tokeniser for doubles.
|
||||
*
|
||||
* @author Andy Hind
|
||||
*/
|
||||
public class DoubleTokenFilter extends Tokenizer
|
||||
{
|
||||
Tokenizer baseTokeniser;
|
||||
|
||||
public DoubleTokenFilter(Reader in)
|
||||
{
|
||||
super(in);
|
||||
baseTokeniser = new StandardTokenizer(in);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.apache.lucene.analysis.TokenStream#next()
|
||||
*/
|
||||
|
||||
public Token next() throws IOException
|
||||
{
|
||||
Token candidate;
|
||||
while((candidate = baseTokeniser.next()) != null)
|
||||
{
|
||||
Double d = Double.valueOf(candidate.termText());
|
||||
String valueString = NumericEncoder.encode(d.doubleValue());
|
||||
Token doubleToken = new Token(valueString, candidate.startOffset(), candidate.startOffset(),
|
||||
candidate.type());
|
||||
return doubleToken;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.analysis;
|
||||
|
||||
import java.io.Reader;
|
||||
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.analysis.TokenStream;
|
||||
|
||||
/**
|
||||
* Simple analyser for floats.
|
||||
*
|
||||
* @author Andy Hind
|
||||
*/
|
||||
public class FloatAnalyser extends Analyzer
|
||||
{
|
||||
|
||||
public FloatAnalyser()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
public TokenStream tokenStream(String fieldName, Reader reader)
|
||||
{
|
||||
return new FloatTokenFilter(reader);
|
||||
}
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.analysis;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
|
||||
import org.apache.lucene.analysis.Token;
|
||||
import org.apache.lucene.analysis.Tokenizer;
|
||||
import org.apache.lucene.analysis.standard.StandardTokenizer;
|
||||
|
||||
/**
|
||||
* Simple tokeniser for floats.
|
||||
*
|
||||
* @author Andy Hind
|
||||
*/
|
||||
public class FloatTokenFilter extends Tokenizer
|
||||
{
|
||||
Tokenizer baseTokeniser;
|
||||
|
||||
public FloatTokenFilter(Reader in)
|
||||
{
|
||||
super(in);
|
||||
baseTokeniser = new StandardTokenizer(in);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.apache.lucene.analysis.TokenStream#next()
|
||||
*/
|
||||
|
||||
public Token next() throws IOException
|
||||
{
|
||||
Token candidate;
|
||||
while((candidate = baseTokeniser.next()) != null)
|
||||
{
|
||||
Float floatValue = Float.valueOf(candidate.termText());
|
||||
String valueString = NumericEncoder.encode(floatValue.floatValue());
|
||||
Token floatToken = new Token(valueString, candidate.startOffset(), candidate.startOffset(),
|
||||
candidate.type());
|
||||
return floatToken;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.analysis;
|
||||
|
||||
import java.io.Reader;
|
||||
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.analysis.TokenStream;
|
||||
|
||||
/**
|
||||
* Simple analyser for integers.
|
||||
*
|
||||
* @author Andy Hind
|
||||
*/
|
||||
public class IntegerAnalyser extends Analyzer
|
||||
{
|
||||
|
||||
public IntegerAnalyser()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
public TokenStream tokenStream(String fieldName, Reader reader)
|
||||
{
|
||||
return new IntegerTokenFilter(reader);
|
||||
}
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.analysis;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
|
||||
import org.apache.lucene.analysis.Token;
|
||||
import org.apache.lucene.analysis.Tokenizer;
|
||||
import org.apache.lucene.analysis.standard.StandardTokenizer;
|
||||
|
||||
/**
|
||||
* Simple tokeniser for integers.
|
||||
*
|
||||
* @author Andy Hind
|
||||
*/
|
||||
public class IntegerTokenFilter extends Tokenizer
|
||||
{
|
||||
Tokenizer baseTokeniser;
|
||||
|
||||
public IntegerTokenFilter(Reader in)
|
||||
{
|
||||
super(in);
|
||||
baseTokeniser = new StandardTokenizer(in);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.apache.lucene.analysis.TokenStream#next()
|
||||
*/
|
||||
|
||||
public Token next() throws IOException
|
||||
{
|
||||
Token candidate;
|
||||
while((candidate = baseTokeniser.next()) != null)
|
||||
{
|
||||
Integer integer = Integer.valueOf(candidate.termText());
|
||||
String valueString = NumericEncoder.encode(integer.intValue());
|
||||
Token integerToken = new Token(valueString, candidate.startOffset(), candidate.startOffset(),
|
||||
candidate.type());
|
||||
return integerToken;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.analysis;
|
||||
|
||||
import java.io.Reader;
|
||||
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.analysis.TokenStream;
|
||||
|
||||
/**
|
||||
* Simple analyser for longs.
|
||||
*
|
||||
* @author Andy Hind
|
||||
*/
|
||||
public class LongAnalyser extends Analyzer
|
||||
{
|
||||
|
||||
public LongAnalyser()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
|
||||
public TokenStream tokenStream(String fieldName, Reader reader)
|
||||
{
|
||||
return new LongTokenFilter(reader);
|
||||
}
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.analysis;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
|
||||
import org.apache.lucene.analysis.Token;
|
||||
import org.apache.lucene.analysis.Tokenizer;
|
||||
import org.apache.lucene.analysis.standard.StandardTokenizer;
|
||||
|
||||
/**
|
||||
* Simple tokeniser for longs.
|
||||
*
|
||||
* @author Andy Hind
|
||||
*/
|
||||
public class LongTokenFilter extends Tokenizer
|
||||
{
|
||||
Tokenizer baseTokeniser;
|
||||
|
||||
public LongTokenFilter(Reader in)
|
||||
{
|
||||
super(in);
|
||||
baseTokeniser = new StandardTokenizer(in);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.apache.lucene.analysis.TokenStream#next()
|
||||
*/
|
||||
|
||||
public Token next() throws IOException
|
||||
{
|
||||
Token candidate;
|
||||
while((candidate = baseTokeniser.next()) != null)
|
||||
{
|
||||
Long longValue = Long.valueOf(candidate.termText());
|
||||
String valueString = NumericEncoder.encode(longValue.longValue());
|
||||
Token longToken = new Token(valueString, candidate.startOffset(), candidate.startOffset(),
|
||||
candidate.type());
|
||||
return longToken;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.analysis;
|
||||
|
||||
/**
|
||||
* Support to encode numeric types in the lucene index.
|
||||
*
|
||||
* To support range queries in the lucene index numeric types need to be indexed
|
||||
* specially. This has been addressed for int and long types for lucene and
|
||||
* limited support (via scaling) for float and double.
|
||||
*
|
||||
* The implementation splits an int, long, float or double into the sign bit,
|
||||
* optional exponent and mantissa either from the int or long format or its IEEE
|
||||
* 754 byte representation.
|
||||
*
|
||||
* To index content so small negative numbers are indexed correctly and are
|
||||
* after big negative numbers in range queries.
|
||||
*
|
||||
* The algorithm finds the sign, if the number is negative, then the mantissa
|
||||
* and exponent are XORed against the appropriate masks. This reverses the
|
||||
* order. As negative numbers appear first in the list their sign bit is 0 and
|
||||
* positive numbers are 1.
|
||||
*
|
||||
* @author Andy Hind
|
||||
*/
|
||||
public class NumericEncoder
|
||||
{
|
||||
/*
|
||||
* Constants for integer encoding
|
||||
*/
|
||||
|
||||
static int INTEGER_SIGN_MASK = 0x80000000;
|
||||
|
||||
/*
|
||||
* Constants for long encoding
|
||||
*/
|
||||
|
||||
static long LONG_SIGN_MASK = 0x8000000000000000L;
|
||||
|
||||
/*
|
||||
* Constants for float encoding
|
||||
*/
|
||||
|
||||
static int FLOAT_SIGN_MASK = 0x80000000;
|
||||
|
||||
static int FLOAT_EXPONENT_MASK = 0x7F800000;
|
||||
|
||||
static int FLOAT_MANTISSA_MASK = 0x007FFFFF;
|
||||
|
||||
/*
|
||||
* Constants for double encoding
|
||||
*/
|
||||
|
||||
static long DOUBLE_SIGN_MASK = 0x8000000000000000L;
|
||||
|
||||
static long DOUBLE_EXPONENT_MASK = 0x7FF0000000000000L;
|
||||
|
||||
static long DOUBLE_MANTISSA_MASK = 0x000FFFFFFFFFFFFFL;
|
||||
|
||||
private NumericEncoder()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode an integer into a string that orders correctly using string
|
||||
* comparison Integer.MIN_VALUE encodes as 00000000 and MAX_VALUE as
|
||||
* ffffffff.
|
||||
*
|
||||
* @param intToEncode
|
||||
* @return
|
||||
*/
|
||||
public static String encode(int intToEncode)
|
||||
{
|
||||
int replacement = intToEncode ^ INTEGER_SIGN_MASK;
|
||||
return encodeToHex(replacement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a long into a string that orders correctly using string comparison
|
||||
* Long.MIN_VALUE encodes as 0000000000000000 and MAX_VALUE as
|
||||
* ffffffffffffffff.
|
||||
*
|
||||
* @param longToEncode
|
||||
* @return
|
||||
*/
|
||||
public static String encode(long longToEncode)
|
||||
{
|
||||
long replacement = longToEncode ^ LONG_SIGN_MASK;
|
||||
return encodeToHex(replacement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a float into a string that orders correctly according to string
|
||||
* comparison. Note that there is no negative NaN but there are codings that
|
||||
* imply this. So NaN and -Infinity may not compare as expected.
|
||||
*
|
||||
* @param floatToEncode
|
||||
* @return
|
||||
*/
|
||||
public static String encode(float floatToEncode)
|
||||
{
|
||||
int bits = Float.floatToIntBits(floatToEncode);
|
||||
int sign = bits & FLOAT_SIGN_MASK;
|
||||
int exponent = bits & FLOAT_EXPONENT_MASK;
|
||||
int mantissa = bits & FLOAT_MANTISSA_MASK;
|
||||
if (sign != 0)
|
||||
{
|
||||
exponent ^= FLOAT_EXPONENT_MASK;
|
||||
mantissa ^= FLOAT_MANTISSA_MASK;
|
||||
}
|
||||
sign ^= FLOAT_SIGN_MASK;
|
||||
int replacement = sign | exponent | mantissa;
|
||||
return encodeToHex(replacement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a double into a string that orders correctly according to string
|
||||
* comparison. Note that there is no negative NaN but there are codings that
|
||||
* imply this. So NaN and -Infinity may not compare as expected.
|
||||
*
|
||||
* @param doubleToEncode
|
||||
* @return
|
||||
*/
|
||||
public static String encode(double doubleToEncode)
|
||||
{
|
||||
long bits = Double.doubleToLongBits(doubleToEncode);
|
||||
long sign = bits & DOUBLE_SIGN_MASK;
|
||||
long exponent = bits & DOUBLE_EXPONENT_MASK;
|
||||
long mantissa = bits & DOUBLE_MANTISSA_MASK;
|
||||
if (sign != 0)
|
||||
{
|
||||
exponent ^= DOUBLE_EXPONENT_MASK;
|
||||
mantissa ^= DOUBLE_MANTISSA_MASK;
|
||||
}
|
||||
sign ^= DOUBLE_SIGN_MASK;
|
||||
long replacement = sign | exponent | mantissa;
|
||||
return encodeToHex(replacement);
|
||||
}
|
||||
|
||||
private static String encodeToHex(int i)
|
||||
{
|
||||
char[] buf = new char[] { '0', '0', '0', '0', '0', '0', '0', '0' };
|
||||
int charPos = 8;
|
||||
do
|
||||
{
|
||||
buf[--charPos] = DIGITS[i & MASK];
|
||||
i >>>= 4;
|
||||
}
|
||||
while (i != 0);
|
||||
return new String(buf);
|
||||
}
|
||||
|
||||
private static String encodeToHex(long l)
|
||||
{
|
||||
char[] buf = new char[] { '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0' };
|
||||
int charPos = 16;
|
||||
do
|
||||
{
|
||||
buf[--charPos] = DIGITS[(int) l & MASK];
|
||||
l >>>= 4;
|
||||
}
|
||||
while (l != 0);
|
||||
return new String(buf);
|
||||
}
|
||||
|
||||
private static final char[] DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e',
|
||||
'f' };
|
||||
|
||||
private static final int MASK = (1 << 4) - 1;
|
||||
}
|
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.analysis;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class NumericEncodingTest extends TestCase
|
||||
{
|
||||
|
||||
public NumericEncodingTest()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
public NumericEncodingTest(String arg0)
|
||||
{
|
||||
super(arg0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do an exhaustive test for integers
|
||||
*
|
||||
*/
|
||||
public void xtestAllIntegerEncodings()
|
||||
{
|
||||
String lastString = null;
|
||||
String nextString = null;
|
||||
for (long i = Integer.MIN_VALUE; i <= Integer.MAX_VALUE; i++)
|
||||
{
|
||||
nextString = NumericEncoder.encode((int) i);
|
||||
if (lastString != null)
|
||||
{
|
||||
assertFalse(lastString.compareTo(nextString) > 0);
|
||||
}
|
||||
lastString = nextString;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do an exhaustive test for float
|
||||
*
|
||||
*/
|
||||
public void xtestAllFloatEncodings()
|
||||
{
|
||||
Float last = null;
|
||||
Float next = null;
|
||||
String lastString = null;
|
||||
String nextString = null;
|
||||
|
||||
for (int sign = 1; sign >= 0; sign--)
|
||||
{
|
||||
if (sign == 0)
|
||||
{
|
||||
for (int exponent = 0; exponent <= 0xFF; exponent++)
|
||||
{
|
||||
for (int mantissa = 0; mantissa <= 0x007FFFFF; mantissa++)
|
||||
{
|
||||
int bitPattern = sign << 31 | exponent << 23 | mantissa;
|
||||
next = Float.intBitsToFloat(bitPattern);
|
||||
|
||||
if (!next.equals(Float.NaN) && (last != null) && (last.compareTo(next) > 0))
|
||||
{
|
||||
System.err.println(last + " > " + next);
|
||||
}
|
||||
if (!next.equals(Float.NaN))
|
||||
{
|
||||
nextString = NumericEncoder.encode(next);
|
||||
if ((lastString != null) && (lastString.compareTo(nextString) > 0))
|
||||
{
|
||||
System.err.println(lastString + " > " + nextString);
|
||||
}
|
||||
lastString = nextString;
|
||||
}
|
||||
last = next;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int exponent = 0xFF; exponent >= 0; exponent--)
|
||||
{
|
||||
for (int mantissa = 0x007FFFFF; mantissa >= 0; mantissa--)
|
||||
{
|
||||
int bitPattern = sign << 31 | exponent << 23 | mantissa;
|
||||
next = Float.intBitsToFloat(bitPattern);
|
||||
if (!next.equals(Float.NaN) && (last != null) && (last.compareTo(next) > 0))
|
||||
{
|
||||
System.err.println(last + " > " + next);
|
||||
}
|
||||
if (!next.equals(Float.NaN))
|
||||
{
|
||||
nextString = NumericEncoder.encode(next);
|
||||
if ((lastString != null) && (lastString.compareTo(nextString) > 0))
|
||||
{
|
||||
System.err.println(lastString + " > " + nextString);
|
||||
}
|
||||
lastString = nextString;
|
||||
}
|
||||
last = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Sample test for int
|
||||
*/
|
||||
|
||||
public void testIntegerEncoding()
|
||||
{
|
||||
assertEquals("00000000", NumericEncoder.encode(Integer.MIN_VALUE));
|
||||
assertEquals("00000001", NumericEncoder.encode(Integer.MIN_VALUE + 1));
|
||||
assertEquals("7fffffff", NumericEncoder.encode(-1));
|
||||
assertEquals("80000000", NumericEncoder.encode(0));
|
||||
assertEquals("80000001", NumericEncoder.encode(1));
|
||||
assertEquals("fffffffe", NumericEncoder.encode(Integer.MAX_VALUE - 1));
|
||||
assertEquals("ffffffff", NumericEncoder.encode(Integer.MAX_VALUE));
|
||||
}
|
||||
|
||||
/*
|
||||
* Sample test for long
|
||||
*/
|
||||
|
||||
public void testLongEncoding()
|
||||
{
|
||||
assertEquals("0000000000000000", NumericEncoder.encode(Long.MIN_VALUE));
|
||||
assertEquals("0000000000000001", NumericEncoder.encode(Long.MIN_VALUE + 1));
|
||||
assertEquals("7fffffffffffffff", NumericEncoder.encode(-1L));
|
||||
assertEquals("8000000000000000", NumericEncoder.encode(0L));
|
||||
assertEquals("8000000000000001", NumericEncoder.encode(1L));
|
||||
assertEquals("fffffffffffffffe", NumericEncoder.encode(Long.MAX_VALUE - 1));
|
||||
assertEquals("ffffffffffffffff", NumericEncoder.encode(Long.MAX_VALUE));
|
||||
}
|
||||
|
||||
/*
|
||||
* Sample test for float
|
||||
*/
|
||||
|
||||
public void testFloatEncoding()
|
||||
{
|
||||
assertEquals("007fffff", NumericEncoder.encode(Float.NEGATIVE_INFINITY));
|
||||
assertEquals("00800000", NumericEncoder.encode(-Float.MAX_VALUE));
|
||||
assertEquals("7ffffffe", NumericEncoder.encode(-Float.MIN_VALUE));
|
||||
assertEquals("7fffffff", NumericEncoder.encode(-0f));
|
||||
assertEquals("80000000", NumericEncoder.encode(0f));
|
||||
assertEquals("80000001", NumericEncoder.encode(Float.MIN_VALUE));
|
||||
assertEquals("ff7fffff", NumericEncoder.encode(Float.MAX_VALUE));
|
||||
assertEquals("ff800000", NumericEncoder.encode(Float.POSITIVE_INFINITY));
|
||||
assertEquals("ffc00000", NumericEncoder.encode(Float.NaN));
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Sample test for double
|
||||
*/
|
||||
|
||||
public void testDoubleEncoding()
|
||||
{
|
||||
assertEquals("000fffffffffffff", NumericEncoder.encode(Double.NEGATIVE_INFINITY));
|
||||
assertEquals("0010000000000000", NumericEncoder.encode(-Double.MAX_VALUE));
|
||||
assertEquals("7ffffffffffffffe", NumericEncoder.encode(-Double.MIN_VALUE));
|
||||
assertEquals("7fffffffffffffff", NumericEncoder.encode(-0d));
|
||||
assertEquals("8000000000000000", NumericEncoder.encode(0d));
|
||||
assertEquals("8000000000000001", NumericEncoder.encode(Double.MIN_VALUE));
|
||||
assertEquals("ffefffffffffffff", NumericEncoder.encode(Double.MAX_VALUE));
|
||||
assertEquals("fff0000000000000", NumericEncoder.encode(Double.POSITIVE_INFINITY));
|
||||
assertEquals("fff8000000000000", NumericEncoder.encode(Double.NaN));
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.analysis;
|
||||
|
||||
import java.io.Reader;
|
||||
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.analysis.TokenStream;
|
||||
|
||||
/**
|
||||
* Analyse repository paths
|
||||
*
|
||||
* @author andyh
|
||||
*/
|
||||
public class PathAnalyser extends Analyzer
|
||||
{
|
||||
public TokenStream tokenStream(String fieldName, Reader reader)
|
||||
{
|
||||
return new PathTokenFilter(reader, PathTokenFilter.PATH_SEPARATOR,
|
||||
PathTokenFilter.SEPARATOR_TOKEN_TEXT, PathTokenFilter.NO_NS_TOKEN_TEXT,
|
||||
PathTokenFilter.NAMESPACE_START_DELIMITER, PathTokenFilter.NAMESPACE_END_DELIMITER, true);
|
||||
}
|
||||
}
|
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.analysis;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import org.apache.lucene.analysis.Token;
|
||||
import org.apache.lucene.analysis.Tokenizer;
|
||||
|
||||
/**
|
||||
* @author andyh
|
||||
*
|
||||
* TODO To change the template for this generated type comment go to Window -
|
||||
* Preferences - Java - Code Style - Code Templates
|
||||
*/
|
||||
public class PathTokenFilter extends Tokenizer
|
||||
{
|
||||
public final static String INTEGER_FORMAT = "0000000000";
|
||||
|
||||
public final static char PATH_SEPARATOR = ';';
|
||||
|
||||
public final static char NAMESPACE_START_DELIMITER = '{';
|
||||
|
||||
public final static char NAMESPACE_END_DELIMITER = '}';
|
||||
|
||||
public final static String SEPARATOR_TOKEN_TEXT = ";";
|
||||
|
||||
public final static String NO_NS_TOKEN_TEXT = "<No Namespace>";
|
||||
|
||||
public final static String TOKEN_TYPE_PATH_SEP = "PATH_SEPARATOR";
|
||||
|
||||
public final static String TOKEN_TYPE_PATH_LENGTH = "PATH_LENGTH";
|
||||
|
||||
public final static String TOKEN_TYPE_PATH_ELEMENT_NAME = "PATH_ELEMENT_NAME";
|
||||
|
||||
public final static String TOKEN_TYPE_PATH_ELEMENT_NAMESPACE = "PATH_ELEMENT_NAMESPACE";
|
||||
|
||||
char pathSeparator;
|
||||
|
||||
String separatorTokenText;
|
||||
|
||||
String noNsTokenText;
|
||||
|
||||
char nsStartDelimiter;
|
||||
|
||||
int nsStartDelimiterLength;
|
||||
|
||||
char nsEndDelimiter;
|
||||
|
||||
int nsEndDelimiterLength;
|
||||
|
||||
LinkedList<Token> tokens = new LinkedList<Token>();
|
||||
|
||||
Iterator<Token> it = null;
|
||||
|
||||
private boolean includeNamespace;
|
||||
|
||||
public PathTokenFilter(Reader in, char pathSeparator, String separatorTokenText, String noNsTokenText,
|
||||
char nsStartDelimiter, char nsEndDelimiter, boolean includeNameSpace)
|
||||
{
|
||||
super(in);
|
||||
this.pathSeparator = pathSeparator;
|
||||
this.separatorTokenText = separatorTokenText;
|
||||
this.noNsTokenText = noNsTokenText;
|
||||
this.nsStartDelimiter = nsStartDelimiter;
|
||||
this.nsEndDelimiter = nsEndDelimiter;
|
||||
this.includeNamespace = includeNameSpace;
|
||||
|
||||
this.nsStartDelimiterLength = 1;
|
||||
this.nsEndDelimiterLength = 1;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.apache.lucene.analysis.TokenStream#next()
|
||||
*/
|
||||
|
||||
public Token next() throws IOException
|
||||
{
|
||||
Token nextToken;
|
||||
if (it == null)
|
||||
{
|
||||
buildTokenListAndIterator();
|
||||
}
|
||||
if (it.hasNext())
|
||||
{
|
||||
nextToken = it.next();
|
||||
}
|
||||
else
|
||||
{
|
||||
nextToken = null;
|
||||
}
|
||||
return nextToken;
|
||||
}
|
||||
|
||||
private void buildTokenListAndIterator() throws IOException
|
||||
{
|
||||
NumberFormat nf = new DecimalFormat(INTEGER_FORMAT);
|
||||
|
||||
// Could optimise to read each path ata time - not just all paths
|
||||
int insertCountAt = 0;
|
||||
int lengthCounter = 0;
|
||||
Token t;
|
||||
Token pathSplitToken = null;
|
||||
Token nameToken = null;
|
||||
Token countToken = null;
|
||||
Token namespaceToken = null;
|
||||
while ((t = nextToken()) != null)
|
||||
{
|
||||
String text = t.termText();
|
||||
|
||||
if((text.length() == 0) || text.equals(pathSeparator))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (text.charAt(text.length()-1) == pathSeparator)
|
||||
{
|
||||
text = text.substring(0, text.length() - 1);
|
||||
pathSplitToken = new Token(separatorTokenText, t.startOffset(), t.endOffset(), TOKEN_TYPE_PATH_SEP);
|
||||
pathSplitToken.setPositionIncrement(1);
|
||||
|
||||
}
|
||||
|
||||
int split = -1;
|
||||
|
||||
if ((text.length() > 0) && (text.charAt(0) == nsStartDelimiter))
|
||||
{
|
||||
split = text.indexOf(nsEndDelimiter);
|
||||
}
|
||||
if (split == -1)
|
||||
{
|
||||
namespaceToken = new Token(noNsTokenText, t.startOffset(), t.startOffset(),
|
||||
TOKEN_TYPE_PATH_ELEMENT_NAMESPACE);
|
||||
nameToken = new Token(text, t.startOffset(), t.endOffset(), TOKEN_TYPE_PATH_ELEMENT_NAME);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
namespaceToken = new Token(text.substring(nsStartDelimiterLength, (split + nsEndDelimiterLength - 1)),
|
||||
t.startOffset(), t.startOffset() + split, TOKEN_TYPE_PATH_ELEMENT_NAMESPACE);
|
||||
nameToken = new Token(text.substring(split + nsEndDelimiterLength), t.startOffset() + split
|
||||
+ nsEndDelimiterLength, t.endOffset(), TOKEN_TYPE_PATH_ELEMENT_NAME);
|
||||
}
|
||||
|
||||
namespaceToken.setPositionIncrement(1);
|
||||
nameToken.setPositionIncrement(1);
|
||||
|
||||
if (includeNamespace)
|
||||
{
|
||||
tokens.add(namespaceToken);
|
||||
}
|
||||
tokens.add(nameToken);
|
||||
|
||||
lengthCounter++;
|
||||
|
||||
if (pathSplitToken != null)
|
||||
{
|
||||
|
||||
String countString = nf.format(lengthCounter);
|
||||
countToken = new Token(countString, t.startOffset(), t.endOffset(), TOKEN_TYPE_PATH_SEP);
|
||||
countToken.setPositionIncrement(1);
|
||||
|
||||
tokens.add(insertCountAt, countToken);
|
||||
tokens.add(pathSplitToken);
|
||||
|
||||
lengthCounter = 0;
|
||||
insertCountAt = tokens.size();
|
||||
|
||||
pathSplitToken = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
String countString = nf.format(lengthCounter);
|
||||
countToken = new Token(countString, 0, 0, TOKEN_TYPE_PATH_SEP);
|
||||
countToken.setPositionIncrement(1);
|
||||
|
||||
tokens.add(insertCountAt, countToken);
|
||||
|
||||
if ((tokens.size() == 0) || !(tokens.get(tokens.size() - 1).termText().equals(TOKEN_TYPE_PATH_SEP)))
|
||||
{
|
||||
pathSplitToken = new Token(separatorTokenText, 0, 0, TOKEN_TYPE_PATH_SEP);
|
||||
pathSplitToken.setPositionIncrement(1);
|
||||
tokens.add(pathSplitToken);
|
||||
}
|
||||
|
||||
it = tokens.iterator();
|
||||
}
|
||||
|
||||
int readerPosition = 0;
|
||||
|
||||
private Token nextToken() throws IOException
|
||||
{
|
||||
if(readerPosition == -1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
StringBuilder buffer = new StringBuilder(64);
|
||||
boolean inNameSpace = false;
|
||||
int start = readerPosition;
|
||||
int current;
|
||||
char c;
|
||||
while((current = input.read()) != -1)
|
||||
{
|
||||
c = (char)current;
|
||||
readerPosition++;
|
||||
if(c == nsStartDelimiter)
|
||||
{
|
||||
inNameSpace = true;
|
||||
}
|
||||
else if(c == nsEndDelimiter)
|
||||
{
|
||||
inNameSpace = false;
|
||||
}
|
||||
else if(!inNameSpace && (c == '/'))
|
||||
{
|
||||
return new Token(buffer.toString(), start, readerPosition-1, "QNAME");
|
||||
}
|
||||
buffer.append(c);
|
||||
}
|
||||
readerPosition = -1;
|
||||
if(!inNameSpace)
|
||||
{
|
||||
return new Token(buffer.toString(), start, readerPosition-1, "QNAME");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalStateException("QName terminated incorrectly: "+buffer.toString());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.analysis;
|
||||
|
||||
import java.io.Reader;
|
||||
|
||||
import org.apache.lucene.analysis.CharTokenizer;
|
||||
|
||||
/**
|
||||
* @author andyh
|
||||
*
|
||||
* TODO To change the template for this generated type comment go to Window -
|
||||
* Preferences - Java - Code Style - Code Templates
|
||||
*/
|
||||
public class PathTokeniser extends CharTokenizer
|
||||
{
|
||||
public PathTokeniser(Reader in)
|
||||
{
|
||||
super(in);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.apache.lucene.analysis.CharTokenizer#isTokenChar(char)
|
||||
*/
|
||||
protected boolean isTokenChar(char c)
|
||||
{
|
||||
return (c != '/') && !Character.isWhitespace(c);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.fts;
|
||||
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
|
||||
public interface FTSIndexerAware
|
||||
{
|
||||
|
||||
public void indexCompleted(StoreRef storeRef, int remaining, Exception e);
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.fts;
|
||||
|
||||
public class FTSIndexerException extends RuntimeException
|
||||
{
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 3258134635127912754L;
|
||||
|
||||
public FTSIndexerException()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
public FTSIndexerException(String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
|
||||
public FTSIndexerException(String message, Throwable cause)
|
||||
{
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public FTSIndexerException(Throwable cause)
|
||||
{
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.fts;
|
||||
|
||||
import org.quartz.Job;
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.quartz.JobExecutionException;
|
||||
|
||||
public class FTSIndexerJob implements Job
|
||||
{
|
||||
public FTSIndexerJob()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
public void execute(JobExecutionContext executionContext) throws JobExecutionException
|
||||
{
|
||||
|
||||
FullTextSearchIndexer indexer = (FullTextSearchIndexer)executionContext.getJobDetail().getJobDataMap().get("bean");
|
||||
if(indexer != null)
|
||||
{
|
||||
indexer.index();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.fts;
|
||||
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
|
||||
|
||||
|
||||
public interface FullTextSearchIndexer {
|
||||
|
||||
public abstract void requiresIndex(StoreRef storeRef);
|
||||
|
||||
public abstract void indexCompleted(StoreRef storeRef, int remaining, Exception e);
|
||||
|
||||
public abstract void pause() throws InterruptedException;
|
||||
|
||||
public abstract void resume() throws InterruptedException;
|
||||
|
||||
public abstract void index();
|
||||
|
||||
}
|
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.fts;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.repo.search.impl.lucene.LuceneIndexer;
|
||||
import org.alfresco.repo.search.impl.lucene.LuceneIndexerAndSearcherFactory;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
public class FullTextSearchIndexerImpl implements FTSIndexerAware, FullTextSearchIndexer
|
||||
{
|
||||
private enum State {
|
||||
ACTIVE, PAUSING, PAUSED
|
||||
};
|
||||
|
||||
private static Set<StoreRef> requiresIndex = new LinkedHashSet<StoreRef>();
|
||||
|
||||
private static Set<StoreRef> indexing = new HashSet<StoreRef>();
|
||||
|
||||
LuceneIndexerAndSearcherFactory luceneIndexerAndSearcherFactory;
|
||||
|
||||
private int pauseCount = 0;
|
||||
|
||||
private boolean paused = false;
|
||||
|
||||
public FullTextSearchIndexerImpl()
|
||||
{
|
||||
super();
|
||||
//System.out.println("Created id is "+this);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.alfresco.repo.search.impl.lucene.fts.FullTextSearchIndexer#requiresIndex(org.alfresco.repo.ref.StoreRef)
|
||||
*/
|
||||
public synchronized void requiresIndex(StoreRef storeRef)
|
||||
{
|
||||
requiresIndex.add(storeRef);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.alfresco.repo.search.impl.lucene.fts.FullTextSearchIndexer#indexCompleted(org.alfresco.repo.ref.StoreRef,
|
||||
* int, java.lang.Exception)
|
||||
*/
|
||||
public synchronized void indexCompleted(StoreRef storeRef, int remaining, Exception e)
|
||||
{
|
||||
try
|
||||
{
|
||||
indexing.remove(storeRef);
|
||||
if ((remaining > 0) || (e != null))
|
||||
{
|
||||
requiresIndex(storeRef);
|
||||
}
|
||||
if (e != null)
|
||||
{
|
||||
throw new FTSIndexerException(e);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
//System.out.println("..Index Complete: id is "+this);
|
||||
this.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.alfresco.repo.search.impl.lucene.fts.FullTextSearchIndexer#pause()
|
||||
*/
|
||||
public synchronized void pause() throws InterruptedException
|
||||
{
|
||||
pauseCount++;
|
||||
//System.out.println("..Waiting "+pauseCount+" id is "+this);
|
||||
while ((indexing.size() > 0))
|
||||
{
|
||||
//System.out.println("Pause: Waiting with count of "+indexing.size()+" id is "+this);
|
||||
this.wait();
|
||||
}
|
||||
pauseCount--;
|
||||
if(pauseCount == 0)
|
||||
{
|
||||
paused = true;
|
||||
this.notifyAll(); // only resumers
|
||||
}
|
||||
//System.out.println("..Remaining "+pauseCount +" paused = "+paused+" id is "+this);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.alfresco.repo.search.impl.lucene.fts.FullTextSearchIndexer#resume()
|
||||
*/
|
||||
public synchronized void resume() throws InterruptedException
|
||||
{
|
||||
if(pauseCount == 0)
|
||||
{
|
||||
//System.out.println("Direct resume"+" id is "+this);
|
||||
paused = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
while(pauseCount > 0)
|
||||
{
|
||||
//System.out.println("Reusme waiting on "+pauseCount+" id is "+this);
|
||||
this.wait();
|
||||
}
|
||||
paused = false;
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized boolean isPaused() throws InterruptedException
|
||||
{
|
||||
if(pauseCount == 0)
|
||||
{
|
||||
return paused;
|
||||
}
|
||||
else
|
||||
{
|
||||
while(pauseCount > 0)
|
||||
{
|
||||
this.wait();
|
||||
}
|
||||
return paused;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.alfresco.repo.search.impl.lucene.fts.FullTextSearchIndexer#index()
|
||||
*/
|
||||
public void index()
|
||||
{
|
||||
// Use the calling thread to index
|
||||
// Parallel indexing via multiple Quartz thread initiating indexing
|
||||
|
||||
StoreRef toIndex = getNextRef();
|
||||
if (toIndex != null)
|
||||
{
|
||||
//System.out.println("Indexing "+toIndex+" id is "+this);
|
||||
LuceneIndexer indexer = luceneIndexerAndSearcherFactory.getIndexer(toIndex);
|
||||
indexer.registerCallBack(this);
|
||||
indexer.updateFullTextSearch(1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
//System.out.println("Nothing to index"+" id is "+this);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized StoreRef getNextRef()
|
||||
{
|
||||
if (paused || (pauseCount > 0))
|
||||
{
|
||||
//System.out.println("Indexing suspended"+" id is "+this);
|
||||
return null;
|
||||
}
|
||||
|
||||
StoreRef nextStoreRef = null;
|
||||
|
||||
for (StoreRef ref : requiresIndex)
|
||||
{
|
||||
if (!indexing.contains(ref))
|
||||
{
|
||||
nextStoreRef = ref;
|
||||
}
|
||||
}
|
||||
|
||||
if (nextStoreRef != null)
|
||||
{
|
||||
requiresIndex.remove(nextStoreRef);
|
||||
indexing.add(nextStoreRef);
|
||||
}
|
||||
|
||||
return nextStoreRef;
|
||||
}
|
||||
|
||||
public void setLuceneIndexerAndSearcherFactory(LuceneIndexerAndSearcherFactory luceneIndexerAndSearcherFactory)
|
||||
{
|
||||
this.luceneIndexerAndSearcherFactory = luceneIndexerAndSearcherFactory;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws InterruptedException
|
||||
{
|
||||
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:alfresco/application-context.xml");
|
||||
}
|
||||
}
|
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.query;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This class patches a term at a specified location.
|
||||
*
|
||||
* @author andyh
|
||||
*/
|
||||
public class AbsoluteStructuredFieldPosition extends AbstractStructuredFieldPosition
|
||||
{
|
||||
|
||||
int requiredPosition;
|
||||
|
||||
/**
|
||||
* Search for a term at the specified position.
|
||||
*/
|
||||
|
||||
public AbsoluteStructuredFieldPosition(String termText, int position)
|
||||
{
|
||||
super(termText, true, true);
|
||||
this.requiredPosition = position;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.alfresco.lucene.extensions.StructuredFieldPosition#matches(int,
|
||||
* org.apache.lucene.index.TermPositions)
|
||||
*/
|
||||
public int matches(int start, int end, int offset) throws IOException
|
||||
{
|
||||
if (offset >= requiredPosition)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (getCachingTermPositions() != null)
|
||||
{
|
||||
// Doing "termText"
|
||||
getCachingTermPositions().reset();
|
||||
int count = getCachingTermPositions().freq();
|
||||
int realPosition = 0;
|
||||
int adjustedPosition = 0;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
realPosition = getCachingTermPositions().nextPosition();
|
||||
adjustedPosition = realPosition - start;
|
||||
if ((end != -1) && (realPosition > end))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (adjustedPosition > requiredPosition)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (adjustedPosition == requiredPosition)
|
||||
{
|
||||
return adjustedPosition;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Doing "*"
|
||||
if ((offset + 1) == requiredPosition)
|
||||
{
|
||||
return offset + 1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.alfresco.lucene.extensions.StructuredFieldPosition#getPosition()
|
||||
*/
|
||||
public int getPosition()
|
||||
{
|
||||
return requiredPosition;
|
||||
}
|
||||
|
||||
public String getDescription()
|
||||
{
|
||||
return "Absolute Named child";
|
||||
}
|
||||
}
|
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.query;
|
||||
|
||||
public abstract class AbstractStructuredFieldPosition implements StructuredFieldPosition
|
||||
{
|
||||
private String termText;
|
||||
|
||||
private boolean isTerminal;
|
||||
|
||||
private boolean isAbsolute;
|
||||
|
||||
private CachingTermPositions tps;
|
||||
|
||||
public AbstractStructuredFieldPosition(String termText, boolean isTerminal, boolean isAbsolute)
|
||||
{
|
||||
super();
|
||||
this.termText = termText;
|
||||
this.isTerminal = isTerminal;
|
||||
this.isAbsolute = isAbsolute;
|
||||
}
|
||||
|
||||
public boolean isTerminal()
|
||||
{
|
||||
return isTerminal;
|
||||
}
|
||||
|
||||
protected void setTerminal(boolean isTerminal)
|
||||
{
|
||||
this.isTerminal = isTerminal;
|
||||
}
|
||||
|
||||
public boolean isAbsolute()
|
||||
{
|
||||
return isAbsolute;
|
||||
}
|
||||
|
||||
public boolean isRelative()
|
||||
{
|
||||
return !isAbsolute;
|
||||
}
|
||||
|
||||
public String getTermText()
|
||||
{
|
||||
return termText;
|
||||
}
|
||||
|
||||
public int getPosition()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void setCachingTermPositions(CachingTermPositions tps)
|
||||
{
|
||||
this.tps = tps;
|
||||
}
|
||||
|
||||
public CachingTermPositions getCachingTermPositions()
|
||||
{
|
||||
return this.tps;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public boolean allowsLinkingBySelf()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean allowslinkingByParent()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean linkParent()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean linkSelf()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
StringBuffer buffer = new StringBuffer(256);
|
||||
buffer.append(getDescription());
|
||||
buffer.append("<"+getTermText()+"> at "+getPosition());
|
||||
buffer.append(" Terminal = "+isTerminal());
|
||||
buffer.append(" Absolute = "+isAbsolute());
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
public abstract String getDescription();
|
||||
|
||||
public boolean isDescendant()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean matchesAll()
|
||||
{
|
||||
return getCachingTermPositions() == null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.query;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author andyh
|
||||
*
|
||||
* TODO To change the template for this generated type comment go to Window -
|
||||
* Preferences - Java - Code Style - Code Templates
|
||||
*/
|
||||
public class AnyStructuredFieldPosition extends AbstractStructuredFieldPosition
|
||||
{
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public AnyStructuredFieldPosition(String termText)
|
||||
{
|
||||
super(termText, true, false);
|
||||
if (termText == null)
|
||||
{
|
||||
setTerminal(false);
|
||||
}
|
||||
}
|
||||
|
||||
public AnyStructuredFieldPosition()
|
||||
{
|
||||
super(null, false, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.alfresco.lucene.extensions.StructuredFieldPosition#matches(int,
|
||||
* int, org.apache.lucene.index.TermPositions)
|
||||
*/
|
||||
public int matches(int start, int end, int offset) throws IOException
|
||||
{
|
||||
// we are doing //name
|
||||
if (getCachingTermPositions() != null)
|
||||
{
|
||||
setTerminal(true);
|
||||
int realPosition = 0;
|
||||
int adjustedPosition = 0;
|
||||
getCachingTermPositions().reset();
|
||||
int count = getCachingTermPositions().freq();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
realPosition = getCachingTermPositions().nextPosition();
|
||||
adjustedPosition = realPosition - start;
|
||||
if ((end != -1) && (realPosition > end))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (adjustedPosition > offset)
|
||||
{
|
||||
return adjustedPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// we are doing //
|
||||
setTerminal(false);
|
||||
return offset;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public String getDescription()
|
||||
{
|
||||
return "Any";
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.query;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.index.TermEnum;
|
||||
import org.apache.lucene.index.TermPositions;
|
||||
|
||||
/**
|
||||
* @author andyh
|
||||
*
|
||||
* TODO To change the template for this generated type comment go to Window -
|
||||
* Preferences - Java - Code Style - Code Templates
|
||||
*/
|
||||
public class CachingTermPositions implements TermPositions
|
||||
{
|
||||
int[] results;
|
||||
|
||||
int position = -1;
|
||||
|
||||
int last = -1;
|
||||
|
||||
TermPositions delegate;
|
||||
|
||||
CachingTermPositions(TermPositions delegate)
|
||||
{
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.apache.lucene.index.TermPositions#nextPosition()
|
||||
*/
|
||||
public int nextPosition() throws IOException
|
||||
{
|
||||
if (results == null)
|
||||
{
|
||||
results = new int[freq()];
|
||||
}
|
||||
position++;
|
||||
if (last < position)
|
||||
{
|
||||
results[position] = delegate.nextPosition();
|
||||
last = position;
|
||||
}
|
||||
return results[position];
|
||||
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
position = -1;
|
||||
}
|
||||
|
||||
private void clear()
|
||||
{
|
||||
position = -1;
|
||||
last = -1;
|
||||
results = null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.apache.lucene.index.TermDocs#seek(org.apache.lucene.index.Term)
|
||||
*/
|
||||
public void seek(Term term) throws IOException
|
||||
{
|
||||
delegate.seek(term);
|
||||
clear();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.apache.lucene.index.TermDocs#seek(org.apache.lucene.index.TermEnum)
|
||||
*/
|
||||
public void seek(TermEnum termEnum) throws IOException
|
||||
{
|
||||
delegate.seek(termEnum);
|
||||
clear();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.apache.lucene.index.TermDocs#doc()
|
||||
*/
|
||||
public int doc()
|
||||
{
|
||||
return delegate.doc();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.apache.lucene.index.TermDocs#freq()
|
||||
*/
|
||||
public int freq()
|
||||
{
|
||||
return delegate.freq();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.apache.lucene.index.TermDocs#next()
|
||||
*/
|
||||
public boolean next() throws IOException
|
||||
{
|
||||
if (delegate.next())
|
||||
{
|
||||
clear();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.apache.lucene.index.TermDocs#read(int[], int[])
|
||||
*/
|
||||
public int read(int[] docs, int[] freqs) throws IOException
|
||||
{
|
||||
int answer = delegate.read(docs, freqs);
|
||||
clear();
|
||||
return answer;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.apache.lucene.index.TermDocs#skipTo(int)
|
||||
*/
|
||||
public boolean skipTo(int target) throws IOException
|
||||
{
|
||||
if (delegate.skipTo(target))
|
||||
{
|
||||
clear();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.apache.lucene.index.TermDocs#close()
|
||||
*/
|
||||
public void close() throws IOException
|
||||
{
|
||||
delegate.close();
|
||||
clear();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,553 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.query;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.lucene.index.TermPositions;
|
||||
import org.apache.lucene.search.Explanation;
|
||||
import org.apache.lucene.search.Scorer;
|
||||
import org.apache.lucene.search.Similarity;
|
||||
import org.apache.lucene.search.Weight;
|
||||
|
||||
/**
|
||||
* The scorer for structured field queries.
|
||||
*
|
||||
* A document either matches or it does not, there for the frequency is reported
|
||||
* as 0.0f or 1.0.
|
||||
*
|
||||
*
|
||||
*
|
||||
* @author andyh
|
||||
*/
|
||||
public class ContainerScorer extends Scorer
|
||||
{
|
||||
// Unused
|
||||
Weight weight;
|
||||
|
||||
// Positions of documents with multiple structure elements
|
||||
// e.g have mutiple paths, multiple categories or multiples entries in the
|
||||
// same category
|
||||
TermPositions root;
|
||||
|
||||
// The Field positions that describe the structure we are trying to match
|
||||
StructuredFieldPosition[] positions;
|
||||
|
||||
// Unused at the moment
|
||||
byte[] norms;
|
||||
|
||||
// The minium document found so far
|
||||
int min = 0;
|
||||
|
||||
// The max document found so far
|
||||
int max = 0;
|
||||
|
||||
// The next root doc
|
||||
// -1 and it has gone off the end
|
||||
int rootDoc = 0;
|
||||
|
||||
// Are there potentially more documents
|
||||
boolean more = true;
|
||||
|
||||
// The frequency of the terms in the doc (0.0f or 1.0f)
|
||||
float freq = 0.0f;
|
||||
|
||||
// A term position to find all container entries (there is no better way of finding the set of rquired containers)
|
||||
private TermPositions containers;
|
||||
|
||||
/**
|
||||
* The arguments here follow the same pattern as used by the PhraseQuery.
|
||||
* (It has the same unused arguments)
|
||||
*
|
||||
* @param weight -
|
||||
* curently unsued
|
||||
* @param tps -
|
||||
* the term positions for the terms we are trying to find
|
||||
* @param root -
|
||||
* the term positions for documents with multiple entries - this
|
||||
* may be null, or contain no matches - it specifies those things
|
||||
* that appear under multiple categories etc.
|
||||
* @param positions -
|
||||
* the structured field positions - where terms should appear
|
||||
* @param similarity -
|
||||
* used in the abstract scorer implementation
|
||||
* @param norms -
|
||||
* unused
|
||||
*/
|
||||
public ContainerScorer(Weight weight, TermPositions root, StructuredFieldPosition[] positions, TermPositions containers, Similarity similarity, byte[] norms)
|
||||
{
|
||||
super(similarity);
|
||||
this.weight = weight;
|
||||
this.positions = positions;
|
||||
this.norms = norms;
|
||||
this.root = root;
|
||||
this.containers = containers;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.apache.lucene.search.Scorer#next()
|
||||
*/
|
||||
public boolean next() throws IOException
|
||||
{
|
||||
// If there is no filtering
|
||||
if (allContainers())
|
||||
{
|
||||
// containers and roots must be in sync or the index is broken
|
||||
while (more)
|
||||
{
|
||||
if (containers.next() && root.next())
|
||||
{
|
||||
if (check(0, root.nextPosition()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
more = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!more)
|
||||
{
|
||||
// One of the search terms has no more docuements
|
||||
return false;
|
||||
}
|
||||
|
||||
if (max == 0)
|
||||
{
|
||||
// We need to initialise
|
||||
// Just do a next on all terms and check if the first doc matches
|
||||
doNextOnAll();
|
||||
if (found())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// drop through to the normal find sequence
|
||||
}
|
||||
|
||||
return findNext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Are we looking for all containers?
|
||||
* If there are no positions we must have a better filter
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private boolean allContainers()
|
||||
{
|
||||
if (positions.length == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
for (StructuredFieldPosition sfp : positions)
|
||||
{
|
||||
if (sfp.getCachingTermPositions() != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
private boolean findNext() throws IOException
|
||||
{
|
||||
// Move to the next document
|
||||
|
||||
while (more)
|
||||
{
|
||||
move(); // may set more to false
|
||||
if (found())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here we must have no more documents
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we have found a match
|
||||
*
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
|
||||
private boolean found() throws IOException
|
||||
{
|
||||
// No predicate test if there are no positions
|
||||
if (positions.length == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// no more documents - no match
|
||||
if (!more)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// min and max must point to the same document
|
||||
if (min != max)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rootDoc != max)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// We have duplicate entries - suport should be improved but it is not used at the moment
|
||||
// This shuld work akin to the leaf scorer
|
||||
// It would compact the index
|
||||
// The match must be in a known term range
|
||||
int count = root.freq();
|
||||
int start = 0;
|
||||
int end = -1;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
// First starts at zero
|
||||
start = 0;
|
||||
end = root.nextPosition() ;
|
||||
}
|
||||
else
|
||||
{
|
||||
start = end + 1;
|
||||
end = root.nextPosition() ;
|
||||
}
|
||||
|
||||
if (check(start, end))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// We had checks to do and they all failed.
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* We have all documents at the same state. Now we check the positions of
|
||||
* the terms.
|
||||
*/
|
||||
|
||||
private boolean check(int start, int end) throws IOException
|
||||
{
|
||||
int offset = checkTail(start, end, 0, 0);
|
||||
// Last match may fail
|
||||
if (offset == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check non // ending patterns end at the end of the available pattern
|
||||
if (positions[positions.length - 1].isTerminal())
|
||||
{
|
||||
return ((offset+1) == end);
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For // type pattern matches we need to test patterns of variable greedyness.
|
||||
*
|
||||
*
|
||||
* @param start
|
||||
* @param end
|
||||
* @param currentPosition
|
||||
* @param currentOffset
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
private int checkTail(int start, int end, int currentPosition, int currentOffset) throws IOException
|
||||
{
|
||||
int offset = currentOffset;
|
||||
for (int i = currentPosition, l = positions.length; i < l; i++)
|
||||
{
|
||||
offset = positions[i].matches(start, end, offset);
|
||||
if (offset == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (positions[i].isDescendant())
|
||||
{
|
||||
for (int j = offset; j < end; j++)
|
||||
{
|
||||
int newOffset = checkTail(start, end, i + 1, j);
|
||||
if (newOffset != -1)
|
||||
{
|
||||
return newOffset;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Move to the next position to consider for a match test
|
||||
*/
|
||||
|
||||
private void move() throws IOException
|
||||
{
|
||||
if (min == max)
|
||||
{
|
||||
// If we were at a match just do next on all terms
|
||||
// They all must move on
|
||||
doNextOnAll();
|
||||
}
|
||||
else
|
||||
{
|
||||
// We are in a range - try and skip to the max position on all terms
|
||||
// Only some need to move on - some may move past the current max and set a new target
|
||||
skipToMax();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Go through all the term positions and try and move to next document. Any
|
||||
* failure measn we have no more.
|
||||
*
|
||||
* This can be used at initialisation and when moving away from an existing
|
||||
* match.
|
||||
*
|
||||
* This will set min, max, more and rootDoc
|
||||
*
|
||||
*/
|
||||
private void doNextOnAll() throws IOException
|
||||
{
|
||||
// Do the terms
|
||||
int current;
|
||||
boolean first = true;
|
||||
for (int i = 0, l = positions.length; i < l; i++)
|
||||
{
|
||||
if (positions[i].getCachingTermPositions() != null)
|
||||
{
|
||||
if (positions[i].getCachingTermPositions().next())
|
||||
|
||||
{
|
||||
current = positions[i].getCachingTermPositions().doc();
|
||||
adjustMinMax(current, first);
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
more = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do the root term - it must always exists as the path could well have mutiple entries
|
||||
// If an entry in the index does not have a root terminal it is broken
|
||||
if (root.next())
|
||||
{
|
||||
rootDoc = root.doc();
|
||||
}
|
||||
else
|
||||
{
|
||||
more = false;
|
||||
return;
|
||||
}
|
||||
if (root.doc() < max)
|
||||
{
|
||||
if (root.skipTo(max))
|
||||
{
|
||||
rootDoc = root.doc();
|
||||
}
|
||||
else
|
||||
{
|
||||
more = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Try and skip all those term positions at documents less than the current
|
||||
* max up to value. This is quite likely to fail and leave us with (min !=
|
||||
* max) but that is OK, we try again.
|
||||
*
|
||||
* It is possible that max increases as we process terms, this is OK. We
|
||||
* just failed to skip to a given value of max and start doing the next.
|
||||
*/
|
||||
private void skipToMax() throws IOException
|
||||
{
|
||||
// Do the terms
|
||||
int current;
|
||||
for (int i = 0, l = positions.length; i < l; i++)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
min = max;
|
||||
}
|
||||
if (positions[i].getCachingTermPositions() != null)
|
||||
{
|
||||
if (positions[i].getCachingTermPositions().doc() < max)
|
||||
{
|
||||
if (positions[i].getCachingTermPositions().skipTo(max))
|
||||
{
|
||||
current = positions[i].getCachingTermPositions().doc();
|
||||
adjustMinMax(current, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
more = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do the root
|
||||
if (root.doc() < max)
|
||||
{
|
||||
if (root.skipTo(max))
|
||||
{
|
||||
rootDoc = root.doc();
|
||||
}
|
||||
else
|
||||
{
|
||||
more = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Adjust the min and max values Convenience boolean to set or adjust the
|
||||
* minimum.
|
||||
*/
|
||||
private void adjustMinMax(int doc, boolean setMin)
|
||||
{
|
||||
|
||||
if (max < doc)
|
||||
{
|
||||
max = doc;
|
||||
}
|
||||
|
||||
if (setMin)
|
||||
{
|
||||
min = doc;
|
||||
}
|
||||
else if (min > doc)
|
||||
{
|
||||
min = doc;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.apache.lucene.search.Scorer#doc()
|
||||
*/
|
||||
public int doc()
|
||||
{
|
||||
if (allContainers())
|
||||
{
|
||||
return containers.doc();
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.apache.lucene.search.Scorer#score()
|
||||
*/
|
||||
public float score() throws IOException
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.apache.lucene.search.Scorer#skipTo(int)
|
||||
*/
|
||||
public boolean skipTo(int target) throws IOException
|
||||
{
|
||||
if (allContainers())
|
||||
{
|
||||
containers.skipTo(target);
|
||||
root.skipTo(containers.doc()); // must match
|
||||
if (check(0, root.nextPosition()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
while (more)
|
||||
{
|
||||
if (containers.next() && root.next())
|
||||
{
|
||||
if (check(0, root.nextPosition()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
more = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
max = target;
|
||||
return findNext();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.apache.lucene.search.Scorer#explain(int)
|
||||
*/
|
||||
public Explanation explain(int doc) throws IOException
|
||||
{
|
||||
// TODO: Work out what a proper explanation would be here?
|
||||
Explanation tfExplanation = new Explanation();
|
||||
|
||||
while (next() && doc() < doc)
|
||||
{
|
||||
}
|
||||
|
||||
float phraseFreq = (doc() == doc) ? freq : 0.0f;
|
||||
tfExplanation.setValue(getSimilarity().tf(phraseFreq));
|
||||
tfExplanation.setDescription("tf(phraseFreq=" + phraseFreq + ")");
|
||||
|
||||
return tfExplanation;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.query;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.index.MultiReader;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.index.TermDocs;
|
||||
import org.apache.lucene.index.TermEnum;
|
||||
import org.apache.lucene.index.TermPositions;
|
||||
|
||||
public class DeltaReader extends MultiReader
|
||||
{
|
||||
int[][] deletions;
|
||||
|
||||
Boolean hasExclusions = null;
|
||||
|
||||
private IndexReader[] subReaders;
|
||||
|
||||
private int maxDoc = 0;
|
||||
|
||||
private int[] starts;
|
||||
|
||||
public DeltaReader(IndexReader[] readers, int[][] deletions) throws IOException
|
||||
{
|
||||
super(readers);
|
||||
this.deletions = deletions;
|
||||
initialize(readers);
|
||||
}
|
||||
|
||||
private void initialize(IndexReader[] subReaders) throws IOException
|
||||
{
|
||||
this.subReaders = subReaders;
|
||||
starts = new int[subReaders.length + 1]; // build starts array
|
||||
for (int i = 0; i < subReaders.length; i++)
|
||||
{
|
||||
starts[i] = maxDoc;
|
||||
maxDoc += subReaders[i].maxDoc(); // compute maxDocs
|
||||
}
|
||||
starts[subReaders.length] = maxDoc;
|
||||
}
|
||||
|
||||
protected void doCommit() throws IOException
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
protected void doDelete(int arg0) throws IOException
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
protected void doUndeleteAll() throws IOException
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public boolean hasDeletions()
|
||||
{
|
||||
return super.hasDeletions() || hasSearchExclusions();
|
||||
}
|
||||
|
||||
private boolean hasSearchExclusions()
|
||||
{
|
||||
if (hasExclusions == null)
|
||||
{
|
||||
for (int i = 0; i < deletions.length; i++)
|
||||
{
|
||||
if (deletions[i].length > 0)
|
||||
{
|
||||
hasExclusions = new Boolean(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
hasExclusions = new Boolean(false);
|
||||
}
|
||||
return hasExclusions.booleanValue();
|
||||
}
|
||||
|
||||
public boolean isDeleted(int docNumber)
|
||||
{
|
||||
int i = readerIndex(docNumber);
|
||||
return super.isDeleted(docNumber) || (Arrays.binarySearch(deletions[i], docNumber - starts[i]) != -1);
|
||||
}
|
||||
|
||||
private int readerIndex(int n)
|
||||
{ // find reader for doc n:
|
||||
int lo = 0; // search starts array
|
||||
int hi = subReaders.length - 1; // for first element less
|
||||
|
||||
while (hi >= lo)
|
||||
{
|
||||
int mid = (lo + hi) >> 1;
|
||||
int midValue = starts[mid];
|
||||
if (n < midValue)
|
||||
hi = mid - 1;
|
||||
else if (n > midValue)
|
||||
lo = mid + 1;
|
||||
else
|
||||
{ // found a match
|
||||
while (mid + 1 < subReaders.length && starts[mid + 1] == midValue)
|
||||
{
|
||||
mid++; // scan to last match
|
||||
}
|
||||
return mid;
|
||||
}
|
||||
}
|
||||
return hi;
|
||||
}
|
||||
|
||||
public TermDocs termDocs() throws IOException
|
||||
{
|
||||
return new DeletingTermDocs(super.termDocs());
|
||||
}
|
||||
|
||||
public TermPositions termPositions() throws IOException
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private class DeletingTermDocs implements TermDocs
|
||||
{
|
||||
TermDocs delegate;
|
||||
|
||||
DeletingTermDocs(TermDocs delegate)
|
||||
{
|
||||
super();
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
public void seek(Term term) throws IOException
|
||||
{
|
||||
delegate.seek(term);
|
||||
}
|
||||
|
||||
public void seek(TermEnum termEnum) throws IOException
|
||||
{
|
||||
delegate.seek(termEnum);
|
||||
}
|
||||
|
||||
public int doc()
|
||||
{
|
||||
return delegate.doc();
|
||||
}
|
||||
|
||||
public int freq()
|
||||
{
|
||||
return delegate.freq();
|
||||
}
|
||||
|
||||
public boolean next() throws IOException
|
||||
{
|
||||
while (delegate.next())
|
||||
{
|
||||
if (!isDeleted(doc()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int read(int[] docs, int[] freqs) throws IOException
|
||||
{
|
||||
int end;
|
||||
int deletedCount;
|
||||
do
|
||||
{
|
||||
end = delegate.read(docs, freqs);
|
||||
if (end == 0)
|
||||
{
|
||||
return end;
|
||||
}
|
||||
deletedCount = 0;
|
||||
for (int i = 0; i < end; i++)
|
||||
{
|
||||
if (!isDeleted(docs[i]))
|
||||
{
|
||||
deletedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (end == deletedCount);
|
||||
// fix up for deleted
|
||||
int position = 0;
|
||||
for(int i = 0; i < end; i++)
|
||||
{
|
||||
if(!isDeleted(i))
|
||||
{
|
||||
docs[position] = docs[i];
|
||||
freqs[position] = freqs[i];
|
||||
position++;
|
||||
}
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
public boolean skipTo(int docNumber) throws IOException
|
||||
{
|
||||
delegate.skipTo(docNumber);
|
||||
if (!isDeleted(doc()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return next();
|
||||
}
|
||||
}
|
||||
|
||||
public void close() throws IOException
|
||||
{
|
||||
delegate.close();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.query;
|
||||
|
||||
public class DescendantAndSelfStructuredFieldPosition extends AnyStructuredFieldPosition
|
||||
{
|
||||
public DescendantAndSelfStructuredFieldPosition()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
public String getDescription()
|
||||
{
|
||||
return "Descendant and Self Axis";
|
||||
}
|
||||
|
||||
public boolean allowsLinkingBySelf()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isDescendant()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,907 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.query;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.search.SearcherException;
|
||||
import org.alfresco.service.cmr.dictionary.AspectDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.TypeDefinition;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.index.TermPositions;
|
||||
import org.apache.lucene.search.Explanation;
|
||||
import org.apache.lucene.search.Scorer;
|
||||
import org.apache.lucene.search.Similarity;
|
||||
import org.apache.lucene.search.Weight;
|
||||
|
||||
public class LeafScorer extends Scorer
|
||||
{
|
||||
static class Counter
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return "count = " + count;
|
||||
}
|
||||
}
|
||||
|
||||
private int counter;
|
||||
|
||||
private int countInCounter;
|
||||
|
||||
int min = 0;
|
||||
|
||||
int max = 0;
|
||||
|
||||
boolean more = true;
|
||||
|
||||
Scorer containerScorer;
|
||||
|
||||
StructuredFieldPosition[] sfps;
|
||||
|
||||
float freq = 0.0f;
|
||||
|
||||
HashMap<String, Counter> parentIds = new HashMap<String, Counter>();
|
||||
|
||||
HashMap<String, List<String>> categories = new HashMap<String, List<String>>();
|
||||
|
||||
HashMap<String, Counter> selfIds = null;
|
||||
|
||||
boolean hasSelfScorer;
|
||||
|
||||
IndexReader reader;
|
||||
|
||||
private TermPositions allNodes;
|
||||
|
||||
TermPositions level0;
|
||||
|
||||
HashSet<String> selfLinks = new HashSet<String>();
|
||||
|
||||
BitSet selfDocs = new BitSet();
|
||||
|
||||
private TermPositions root;
|
||||
|
||||
private int rootDoc;
|
||||
|
||||
private boolean repeat;
|
||||
|
||||
private DictionaryService dictionaryService;
|
||||
|
||||
private int[] parents;
|
||||
|
||||
private int[] self;
|
||||
|
||||
private int[] cats;
|
||||
|
||||
private TermPositions tp;
|
||||
|
||||
public LeafScorer(Weight weight, TermPositions root, TermPositions level0, ContainerScorer containerScorer,
|
||||
StructuredFieldPosition[] sfps, TermPositions allNodes, HashMap<String, Counter> selfIds,
|
||||
IndexReader reader, Similarity similarity, byte[] norms, DictionaryService dictionaryService,
|
||||
boolean repeat, TermPositions tp)
|
||||
{
|
||||
super(similarity);
|
||||
this.root = root;
|
||||
this.containerScorer = containerScorer;
|
||||
this.sfps = sfps;
|
||||
this.allNodes = allNodes;
|
||||
this.tp = tp;
|
||||
if (selfIds == null)
|
||||
{
|
||||
this.selfIds = new HashMap<String, Counter>();
|
||||
hasSelfScorer = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.selfIds = selfIds;
|
||||
hasSelfScorer = true;
|
||||
}
|
||||
this.reader = reader;
|
||||
this.level0 = level0;
|
||||
this.dictionaryService = dictionaryService;
|
||||
this.repeat = repeat;
|
||||
try
|
||||
{
|
||||
initialise();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new SearcherException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void initialise() throws IOException
|
||||
{
|
||||
if (containerScorer != null)
|
||||
{
|
||||
parentIds.clear();
|
||||
while (containerScorer.next())
|
||||
{
|
||||
int doc = containerScorer.doc();
|
||||
Document document = reader.document(doc);
|
||||
Field id = document.getField("ID");
|
||||
Counter counter = parentIds.get(id.stringValue());
|
||||
if (counter == null)
|
||||
{
|
||||
counter = new Counter();
|
||||
parentIds.put(id.stringValue(), counter);
|
||||
}
|
||||
counter.count++;
|
||||
|
||||
if (!hasSelfScorer)
|
||||
{
|
||||
counter = selfIds.get(id.stringValue());
|
||||
if (counter == null)
|
||||
{
|
||||
counter = new Counter();
|
||||
selfIds.put(id.stringValue(), counter);
|
||||
}
|
||||
counter.count++;
|
||||
}
|
||||
|
||||
Field isCategory = document.getField("ISCATEGORY");
|
||||
if (isCategory != null)
|
||||
{
|
||||
Field path = document.getField("PATH");
|
||||
String pathString = path.stringValue();
|
||||
if ((pathString.length() > 0) && (pathString.charAt(0) == '/'))
|
||||
{
|
||||
pathString = pathString.substring(1);
|
||||
}
|
||||
List<String> list = categories.get(id.stringValue());
|
||||
if (list == null)
|
||||
{
|
||||
list = new ArrayList<String>();
|
||||
categories.put(id.stringValue(), list);
|
||||
}
|
||||
list.add(pathString);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (level0 != null)
|
||||
{
|
||||
parentIds.clear();
|
||||
while (level0.next())
|
||||
{
|
||||
int doc = level0.doc();
|
||||
Document document = reader.document(doc);
|
||||
Field id = document.getField("ID");
|
||||
if (id != null)
|
||||
{
|
||||
Counter counter = parentIds.get(id.stringValue());
|
||||
if (counter == null)
|
||||
{
|
||||
counter = new Counter();
|
||||
parentIds.put(id.stringValue(), counter);
|
||||
}
|
||||
counter.count++;
|
||||
|
||||
counter = selfIds.get(id.stringValue());
|
||||
if (counter == null)
|
||||
{
|
||||
counter = new Counter();
|
||||
selfIds.put(id.stringValue(), counter);
|
||||
}
|
||||
counter.count++;
|
||||
}
|
||||
}
|
||||
if (parentIds.size() != 1)
|
||||
{
|
||||
throw new SearcherException("More than one root node? " + parentIds.size());
|
||||
}
|
||||
}
|
||||
|
||||
if (allNodes())
|
||||
{
|
||||
int position = 0;
|
||||
parents = new int[10000];
|
||||
for (String parent : parentIds.keySet())
|
||||
{
|
||||
Counter counter = parentIds.get(parent);
|
||||
tp.seek(new Term("PARENT", parent));
|
||||
while (tp.next())
|
||||
{
|
||||
for (int i = 0, l = tp.freq(); i < l; i++)
|
||||
{
|
||||
for(int j = 0; j < counter.count; j++)
|
||||
{
|
||||
parents[position++] = tp.doc();
|
||||
if (position == parents.length)
|
||||
{
|
||||
int[] old = parents;
|
||||
parents = new int[old.length * 2];
|
||||
System.arraycopy(old, 0, parents, 0, old.length);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
int[] old = parents;
|
||||
parents = new int[position];
|
||||
System.arraycopy(old, 0, parents, 0, position);
|
||||
Arrays.sort(parents);
|
||||
|
||||
position = 0;
|
||||
self = new int[10000];
|
||||
for (String id : selfIds.keySet())
|
||||
{
|
||||
tp.seek(new Term("ID", id));
|
||||
while (tp.next())
|
||||
{
|
||||
Counter counter = selfIds.get(id);
|
||||
for(int i = 0; i < counter.count; i++)
|
||||
{
|
||||
self[position++] = tp.doc();
|
||||
if (position == self.length)
|
||||
{
|
||||
old = self;
|
||||
self = new int[old.length * 2];
|
||||
System.arraycopy(old, 0, self, 0, old.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
old = self;
|
||||
self = new int[position];
|
||||
System.arraycopy(old, 0, self, 0, position);
|
||||
Arrays.sort(self);
|
||||
|
||||
position = 0;
|
||||
cats = new int[10000];
|
||||
for (String catid : categories.keySet())
|
||||
{
|
||||
for (QName apsectQName : dictionaryService.getAllAspects())
|
||||
{
|
||||
AspectDefinition aspDef = dictionaryService.getAspect(apsectQName);
|
||||
if (isCategorised(aspDef))
|
||||
{
|
||||
for (PropertyDefinition propDef : aspDef.getProperties().values())
|
||||
{
|
||||
if (propDef.getDataType().getName().equals(DataTypeDefinition.CATEGORY))
|
||||
{
|
||||
tp.seek(new Term("@" + propDef.getName().toString(), catid));
|
||||
while (tp.next())
|
||||
{
|
||||
for (int i = 0, l = tp.freq(); i < l; i++)
|
||||
{
|
||||
cats[position++] = tp.doc();
|
||||
if (position == cats.length)
|
||||
{
|
||||
old = cats;
|
||||
cats = new int[old.length * 2];
|
||||
System.arraycopy(old, 0, cats, 0, old.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
old = cats;
|
||||
cats = new int[position];
|
||||
System.arraycopy(old, 0, cats, 0, position);
|
||||
Arrays.sort(cats);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean next() throws IOException
|
||||
{
|
||||
|
||||
if (repeat && (countInCounter < counter))
|
||||
{
|
||||
countInCounter++;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
countInCounter = 1;
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
if (allNodes())
|
||||
{
|
||||
while (more)
|
||||
{
|
||||
if (allNodes.next() && root.next())
|
||||
{
|
||||
if (check())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
more = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!more)
|
||||
{
|
||||
// One of the search terms has no more docuements
|
||||
return false;
|
||||
}
|
||||
|
||||
if (max == 0)
|
||||
{
|
||||
// We need to initialise
|
||||
// Just do a next on all terms and check if the first doc matches
|
||||
doNextOnAll();
|
||||
if (found())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return findNext();
|
||||
}
|
||||
|
||||
private boolean allNodes()
|
||||
{
|
||||
if (sfps.length == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
for (StructuredFieldPosition sfp : sfps)
|
||||
{
|
||||
if (sfp.getCachingTermPositions() != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean findNext() throws IOException
|
||||
{
|
||||
// Move to the next document
|
||||
|
||||
while (more)
|
||||
{
|
||||
move(); // may set more to false
|
||||
if (found())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here we must have no more documents
|
||||
return false;
|
||||
}
|
||||
|
||||
private void skipToMax() throws IOException
|
||||
{
|
||||
// Do the terms
|
||||
int current;
|
||||
for (int i = 0, l = sfps.length; i < l; i++)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
min = max;
|
||||
}
|
||||
if (sfps[i].getCachingTermPositions() != null)
|
||||
{
|
||||
if (sfps[i].getCachingTermPositions().doc() < max)
|
||||
{
|
||||
if (sfps[i].getCachingTermPositions().skipTo(max))
|
||||
{
|
||||
current = sfps[i].getCachingTermPositions().doc();
|
||||
adjustMinMax(current, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
more = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do the root
|
||||
if (root.doc() < max)
|
||||
{
|
||||
if (root.skipTo(max))
|
||||
{
|
||||
rootDoc = root.doc();
|
||||
}
|
||||
else
|
||||
{
|
||||
more = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void move() throws IOException
|
||||
{
|
||||
if (min == max)
|
||||
{
|
||||
// If we were at a match just do next on all terms
|
||||
doNextOnAll();
|
||||
}
|
||||
else
|
||||
{
|
||||
// We are in a range - try and skip to the max position on all terms
|
||||
skipToMax();
|
||||
}
|
||||
}
|
||||
|
||||
private void doNextOnAll() throws IOException
|
||||
{
|
||||
// Do the terms
|
||||
int current;
|
||||
boolean first = true;
|
||||
for (int i = 0, l = sfps.length; i < l; i++)
|
||||
{
|
||||
if (sfps[i].getCachingTermPositions() != null)
|
||||
{
|
||||
if (sfps[i].getCachingTermPositions().next())
|
||||
{
|
||||
current = sfps[i].getCachingTermPositions().doc();
|
||||
adjustMinMax(current, first);
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
more = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do the root term
|
||||
if (root.next())
|
||||
{
|
||||
rootDoc = root.doc();
|
||||
}
|
||||
else
|
||||
{
|
||||
more = false;
|
||||
return;
|
||||
}
|
||||
if (root.doc() < max)
|
||||
{
|
||||
if (root.skipTo(max))
|
||||
{
|
||||
rootDoc = root.doc();
|
||||
}
|
||||
else
|
||||
{
|
||||
more = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void adjustMinMax(int doc, boolean setMin)
|
||||
{
|
||||
|
||||
if (max < doc)
|
||||
{
|
||||
max = doc;
|
||||
}
|
||||
|
||||
if (setMin)
|
||||
{
|
||||
min = doc;
|
||||
}
|
||||
else if (min > doc)
|
||||
{
|
||||
min = doc;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean found() throws IOException
|
||||
{
|
||||
if (sfps.length == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// no more documents - no match
|
||||
if (!more)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// min and max must point to the same document
|
||||
if (min != max)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rootDoc != max)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return check();
|
||||
}
|
||||
|
||||
private boolean check() throws IOException
|
||||
{
|
||||
if (allNodes())
|
||||
{
|
||||
this.counter = 0;
|
||||
int position;
|
||||
|
||||
StructuredFieldPosition last = sfps[sfps.length - 1];
|
||||
|
||||
if (last.linkSelf())
|
||||
{
|
||||
if ((self != null) && sfps[1].linkSelf() && ((position = Arrays.binarySearch(self, doc())) >= 0))
|
||||
{
|
||||
if (!selfDocs.get(doc()))
|
||||
{
|
||||
selfDocs.set(doc());
|
||||
while (position > -1 && self[position] == doc())
|
||||
{
|
||||
position--;
|
||||
}
|
||||
for (int i = position + 1, l = self.length; ((i < l) && (self[i] == doc())); i++)
|
||||
{
|
||||
this.counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!selfDocs.get(doc()) && last.linkParent())
|
||||
{
|
||||
if ((parents != null) && ((position = Arrays.binarySearch(parents, doc())) >= 0))
|
||||
{
|
||||
while (position > -1 && parents[position] == doc())
|
||||
{
|
||||
position--;
|
||||
}
|
||||
for (int i = position + 1, l = parents.length; ((i < l) && (parents[i] == doc())); i++)
|
||||
{
|
||||
this.counter++;
|
||||
}
|
||||
}
|
||||
|
||||
if ((cats != null) && ((position = Arrays.binarySearch(cats, doc())) >= 0))
|
||||
{
|
||||
while (position > -1 && cats[position] == doc())
|
||||
{
|
||||
position--;
|
||||
}
|
||||
for (int i = position + 1, l = cats.length; ((i < l) && (cats[i] == doc())); i++)
|
||||
{
|
||||
this.counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return counter > 0;
|
||||
}
|
||||
|
||||
// String name = reader.document(doc()).getField("QNAME").stringValue();
|
||||
// We have duplicate entries
|
||||
// The match must be in a known term range
|
||||
int count = root.freq();
|
||||
int start = 0;
|
||||
int end = -1;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
// First starts at zero
|
||||
start = 0;
|
||||
end = root.nextPosition();
|
||||
}
|
||||
else
|
||||
{
|
||||
start = end + 1;
|
||||
end = root.nextPosition();
|
||||
}
|
||||
|
||||
check(start, end, i);
|
||||
|
||||
}
|
||||
// We had checks to do and they all failed.
|
||||
return this.counter > 0;
|
||||
}
|
||||
|
||||
private void check(int start, int end, int position) throws IOException
|
||||
{
|
||||
int offset = 0;
|
||||
for (int i = 0, l = sfps.length; i < l; i++)
|
||||
{
|
||||
offset = sfps[i].matches(start, end, offset);
|
||||
if (offset == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Last match may fail
|
||||
if (offset == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((sfps[sfps.length - 1].isTerminal()) && (offset != 2))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Document doc = reader.document(doc());
|
||||
Field[] parentFields = doc.getFields("PARENT");
|
||||
Field[] linkFields = doc.getFields("LINKASPECT");
|
||||
|
||||
String parentID = null;
|
||||
String linkAspect = null;
|
||||
if ((parentFields != null) && (parentFields.length > position) && (parentFields[position] != null))
|
||||
{
|
||||
parentID = parentFields[position].stringValue();
|
||||
}
|
||||
if ((linkFields != null) && (linkFields.length > position) && (linkFields[position] != null))
|
||||
{
|
||||
linkAspect = linkFields[position].stringValue();
|
||||
}
|
||||
|
||||
containersIncludeCurrent(doc, parentID, linkAspect);
|
||||
|
||||
}
|
||||
|
||||
private void containersIncludeCurrent(Document document, String parentID, String aspectQName) throws IOException
|
||||
{
|
||||
if ((containerScorer != null) || (level0 != null))
|
||||
{
|
||||
if (sfps.length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
String id = document.getField("ID").stringValue();
|
||||
StructuredFieldPosition last = sfps[sfps.length - 1];
|
||||
if ((last.linkSelf() && selfIds.containsKey(id)))
|
||||
{
|
||||
Counter counter = selfIds.get(id);
|
||||
if (counter != null)
|
||||
{
|
||||
if (!selfLinks.contains(id))
|
||||
{
|
||||
this.counter += counter.count;
|
||||
selfLinks.add(id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((parentID != null) && (parentID.length() > 0) && last.linkParent())
|
||||
{
|
||||
if (!selfLinks.contains(id))
|
||||
{
|
||||
if (categories.containsKey(parentID))
|
||||
{
|
||||
Field typeField = document.getField("TYPE");
|
||||
if ((typeField != null) && (typeField.stringValue() != null))
|
||||
{
|
||||
QName typeRef = QName.createQName(typeField.stringValue());
|
||||
if (isCategory(typeRef))
|
||||
{
|
||||
Counter counter = parentIds.get(parentID);
|
||||
if (counter != null)
|
||||
{
|
||||
this.counter += counter.count;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (aspectQName != null)
|
||||
{
|
||||
QName classRef = QName.createQName(aspectQName);
|
||||
AspectDefinition aspDef = dictionaryService.getAspect(classRef);
|
||||
if (isCategorised(aspDef))
|
||||
{
|
||||
for (PropertyDefinition propDef : aspDef.getProperties().values())
|
||||
{
|
||||
if (propDef.getDataType().getName().equals(DataTypeDefinition.CATEGORY))
|
||||
{
|
||||
// get field and compare to ID
|
||||
// Check in path as QName
|
||||
// somewhere
|
||||
Field[] categoryFields = document.getFields("@" + propDef.getName());
|
||||
if (categoryFields != null)
|
||||
{
|
||||
for (Field categoryField : categoryFields)
|
||||
{
|
||||
if ((categoryField != null) && (categoryField.stringValue() != null))
|
||||
{
|
||||
if (categoryField.stringValue().endsWith(parentID))
|
||||
{
|
||||
int count = 0;
|
||||
List<String> paths = categories.get(parentID);
|
||||
if (paths != null)
|
||||
{
|
||||
for (String path : paths)
|
||||
{
|
||||
if (path.indexOf(aspectQName) != -1)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.counter += count;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Counter counter = parentIds.get(parentID);
|
||||
if (counter != null)
|
||||
{
|
||||
this.counter += counter.count;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isCategory(QName classRef)
|
||||
{
|
||||
if (classRef == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
TypeDefinition current = dictionaryService.getType(classRef);
|
||||
while (current != null)
|
||||
{
|
||||
if (current.getName().equals(ContentModel.TYPE_CATEGORY))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
QName parentName = current.getParentName();
|
||||
if (parentName == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
current = dictionaryService.getType(parentName);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isCategorised(AspectDefinition aspDef)
|
||||
{
|
||||
AspectDefinition current = aspDef;
|
||||
while (current != null)
|
||||
{
|
||||
if (current.getName().equals(ContentModel.ASPECT_CLASSIFIABLE))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
QName parentName = current.getParentName();
|
||||
if (parentName == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
current = dictionaryService.getAspect(parentName);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int doc()
|
||||
{
|
||||
if (allNodes())
|
||||
{
|
||||
return allNodes.doc();
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
public float score() throws IOException
|
||||
{
|
||||
return repeat ? 1.0f : counter;
|
||||
}
|
||||
|
||||
public boolean skipTo(int target) throws IOException
|
||||
{
|
||||
|
||||
countInCounter = 1;
|
||||
counter = 0;
|
||||
|
||||
if (allNodes())
|
||||
{
|
||||
allNodes.skipTo(target);
|
||||
root.skipTo(allNodes.doc()); // must match
|
||||
if (check())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
while (more)
|
||||
{
|
||||
if (allNodes.next() && root.next())
|
||||
{
|
||||
if (check())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
more = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
max = target;
|
||||
return findNext();
|
||||
}
|
||||
|
||||
public Explanation explain(int doc) throws IOException
|
||||
{
|
||||
Explanation tfExplanation = new Explanation();
|
||||
|
||||
while (next() && doc() < doc)
|
||||
{
|
||||
}
|
||||
|
||||
float phraseFreq = (doc() == doc) ? freq : 0.0f;
|
||||
tfExplanation.setValue(getSimilarity().tf(phraseFreq));
|
||||
tfExplanation.setDescription("tf(phraseFreq=" + phraseFreq + ")");
|
||||
|
||||
return tfExplanation;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.query;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.search.Explanation;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.Scorer;
|
||||
import org.apache.lucene.search.Searcher;
|
||||
import org.apache.lucene.search.Weight;
|
||||
|
||||
/**
|
||||
* An extension to the Lucene query set.
|
||||
*
|
||||
* This query supports structured queries against paths.
|
||||
*
|
||||
* The field must have been tokenised using the path tokeniser.
|
||||
*
|
||||
* This class manages linking together an ordered chain of absolute and relative
|
||||
* positional queries.
|
||||
*
|
||||
* @author Andy Hind
|
||||
*/
|
||||
public class PathQuery extends Query
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 3832904355660707892L;
|
||||
|
||||
private String pathField = "PATH";
|
||||
|
||||
private String qNameField = "QNAME";
|
||||
|
||||
private int unitSize = 2;
|
||||
|
||||
private List<StructuredFieldPosition> pathStructuredFieldPositions = new ArrayList<StructuredFieldPosition>();
|
||||
|
||||
private List<StructuredFieldPosition> qNameStructuredFieldPositions = new ArrayList<StructuredFieldPosition>();
|
||||
|
||||
private DictionaryService dictionarySertvice;
|
||||
|
||||
private boolean repeats = false;
|
||||
|
||||
/**
|
||||
* The base query
|
||||
*
|
||||
* @param query
|
||||
*/
|
||||
|
||||
public PathQuery(DictionaryService dictionarySertvice)
|
||||
{
|
||||
super();
|
||||
this.dictionarySertvice = dictionarySertvice;
|
||||
}
|
||||
|
||||
public void setQuery(List<StructuredFieldPosition> path, List<StructuredFieldPosition> qname)
|
||||
{
|
||||
qNameStructuredFieldPositions.clear();
|
||||
pathStructuredFieldPositions.clear();
|
||||
if (qname.size() != unitSize)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
if (path.size() % unitSize != 0)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
qNameStructuredFieldPositions.addAll(qname);
|
||||
pathStructuredFieldPositions.addAll(path);
|
||||
}
|
||||
|
||||
public void appendQuery(List<StructuredFieldPosition> sfps)
|
||||
{
|
||||
if (sfps.size() != unitSize)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
StructuredFieldPosition last = null;
|
||||
StructuredFieldPosition next = sfps.get(0);
|
||||
|
||||
if (qNameStructuredFieldPositions.size() > 0)
|
||||
{
|
||||
last = qNameStructuredFieldPositions.get(qNameStructuredFieldPositions.size() - 1);
|
||||
}
|
||||
|
||||
if ((last != null) && next.linkParent() && !last.allowslinkingByParent())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((last != null) && next.linkSelf() && !last.allowsLinkingBySelf())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (qNameStructuredFieldPositions.size() == unitSize)
|
||||
{
|
||||
pathStructuredFieldPositions.addAll(qNameStructuredFieldPositions);
|
||||
}
|
||||
qNameStructuredFieldPositions.clear();
|
||||
qNameStructuredFieldPositions.addAll(sfps);
|
||||
}
|
||||
|
||||
public String getPathField()
|
||||
{
|
||||
return pathField;
|
||||
}
|
||||
|
||||
public void setPathField(String pathField)
|
||||
{
|
||||
this.pathField = pathField;
|
||||
}
|
||||
|
||||
public String getQnameField()
|
||||
{
|
||||
return qNameField;
|
||||
}
|
||||
|
||||
public void setQnameField(String qnameField)
|
||||
{
|
||||
this.qNameField = qnameField;
|
||||
}
|
||||
|
||||
public Term getPathRootTerm()
|
||||
{
|
||||
return new Term(getPathField(), ";");
|
||||
}
|
||||
|
||||
public Term getQNameRootTerm()
|
||||
{
|
||||
return new Term(getQnameField(), ";");
|
||||
}
|
||||
|
||||
/*
|
||||
* @see org.apache.lucene.search.Query#createWeight(org.apache.lucene.search.Searcher)
|
||||
*/
|
||||
protected Weight createWeight(Searcher searcher)
|
||||
{
|
||||
return new StructuredFieldWeight(searcher);
|
||||
}
|
||||
|
||||
/*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
/*
|
||||
* @see org.apache.lucene.search.Query#toString(java.lang.String)
|
||||
*/
|
||||
public String toString(String field)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
private class StructuredFieldWeight implements Weight
|
||||
{
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 3257854259645985328L;
|
||||
|
||||
private Searcher searcher;
|
||||
|
||||
private float value;
|
||||
|
||||
private float idf;
|
||||
|
||||
private float queryNorm;
|
||||
|
||||
private float queryWeight;
|
||||
|
||||
public StructuredFieldWeight(Searcher searcher)
|
||||
{
|
||||
this.searcher = searcher;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* @see org.apache.lucene.search.Weight#explain(org.apache.lucene.index.IndexReader,
|
||||
* int)
|
||||
*/
|
||||
public Explanation explain(IndexReader reader, int doc) throws IOException
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/*
|
||||
* @see org.apache.lucene.search.Weight#getQuery()
|
||||
*/
|
||||
public Query getQuery()
|
||||
{
|
||||
return PathQuery.this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.apache.lucene.search.Weight#getValue()
|
||||
*/
|
||||
public float getValue()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.apache.lucene.search.Weight#normalize(float)
|
||||
*/
|
||||
public void normalize(float queryNorm)
|
||||
{
|
||||
this.queryNorm = queryNorm;
|
||||
queryWeight *= queryNorm; // normalize query weight
|
||||
value = queryWeight * idf; // idf for document
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.apache.lucene.search.Weight#scorer(org.apache.lucene.index.IndexReader)
|
||||
*/
|
||||
public Scorer scorer(IndexReader reader) throws IOException
|
||||
{
|
||||
return PathScorer.createPathScorer(getSimilarity(searcher), PathQuery.this, reader, this, dictionarySertvice, repeats);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.apache.lucene.search.Weight#sumOfSquaredWeights()
|
||||
*/
|
||||
public float sumOfSquaredWeights() throws IOException
|
||||
{
|
||||
idf = getSimilarity(searcher).idf(getTerms(), searcher); // compute
|
||||
// idf
|
||||
queryWeight = idf * getBoost(); // compute query weight
|
||||
return queryWeight * queryWeight; // square it
|
||||
}
|
||||
|
||||
private ArrayList<Term> getTerms()
|
||||
{
|
||||
ArrayList<Term> answer = new ArrayList<Term>(pathStructuredFieldPositions.size());
|
||||
for (StructuredFieldPosition sfp : pathStructuredFieldPositions)
|
||||
{
|
||||
if (sfp.getTermText() != null)
|
||||
{
|
||||
Term term = new Term(pathField, sfp.getTermText());
|
||||
answer.add(term);
|
||||
}
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
}
|
||||
|
||||
public void removeDescendantAndSelf()
|
||||
{
|
||||
while ((getLast() != null) && getLast().linkSelf())
|
||||
{
|
||||
removeLast();
|
||||
removeLast();
|
||||
}
|
||||
}
|
||||
|
||||
private StructuredFieldPosition getLast()
|
||||
|
||||
{
|
||||
if (qNameStructuredFieldPositions.size() > 0)
|
||||
{
|
||||
return qNameStructuredFieldPositions.get(qNameStructuredFieldPositions.size() - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void removeLast()
|
||||
{
|
||||
qNameStructuredFieldPositions.clear();
|
||||
for (int i = 0; i < unitSize; i++)
|
||||
{
|
||||
if (pathStructuredFieldPositions.size() > 0)
|
||||
{
|
||||
qNameStructuredFieldPositions.add(0, pathStructuredFieldPositions.remove(pathStructuredFieldPositions.size() - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return qNameStructuredFieldPositions.size() == 0;
|
||||
}
|
||||
|
||||
public List<StructuredFieldPosition> getPathStructuredFieldPositions()
|
||||
{
|
||||
return pathStructuredFieldPositions;
|
||||
}
|
||||
|
||||
|
||||
public List<StructuredFieldPosition> getQNameStructuredFieldPositions()
|
||||
{
|
||||
return qNameStructuredFieldPositions;
|
||||
}
|
||||
|
||||
public void setRepeats(boolean repeats)
|
||||
{
|
||||
this.repeats = repeats;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.query;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.alfresco.repo.search.impl.lucene.query.LeafScorer.Counter;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.index.TermPositions;
|
||||
import org.apache.lucene.search.Explanation;
|
||||
import org.apache.lucene.search.Scorer;
|
||||
import org.apache.lucene.search.Similarity;
|
||||
import org.apache.lucene.search.Weight;
|
||||
|
||||
public class PathScorer extends Scorer
|
||||
{
|
||||
Scorer scorer;
|
||||
|
||||
PathScorer(Similarity similarity, Scorer scorer)
|
||||
{
|
||||
super(similarity);
|
||||
this.scorer = scorer;
|
||||
}
|
||||
|
||||
|
||||
public static PathScorer createPathScorer(Similarity similarity, PathQuery pathQuery, IndexReader reader, Weight weight, DictionaryService dictionarySertvice, boolean repeat) throws IOException
|
||||
{
|
||||
Scorer selfScorer = null;
|
||||
HashMap<String, Counter> selfIds = null;
|
||||
|
||||
StructuredFieldPosition last = null;
|
||||
if(pathQuery.getQNameStructuredFieldPositions().size() > 0)
|
||||
{
|
||||
last = pathQuery.getQNameStructuredFieldPositions().get(pathQuery.getQNameStructuredFieldPositions().size() - 1);
|
||||
}
|
||||
if ((last != null) && last.linkSelf())
|
||||
{
|
||||
PathQuery selfQuery = new PathQuery(dictionarySertvice);
|
||||
selfQuery.setQuery(pathQuery.getPathStructuredFieldPositions(), pathQuery.getQNameStructuredFieldPositions());
|
||||
selfQuery.removeDescendantAndSelf();
|
||||
if (!selfQuery.isEmpty())
|
||||
{
|
||||
selfIds = new HashMap<String, Counter>();
|
||||
selfScorer = PathScorer.createPathScorer(similarity, selfQuery, reader, weight, dictionarySertvice, repeat);
|
||||
selfIds.clear();
|
||||
while (selfScorer.next())
|
||||
{
|
||||
int doc = selfScorer.doc();
|
||||
Document document = reader.document(doc);
|
||||
Field id = document.getField("ID");
|
||||
Counter counter = selfIds.get(id.stringValue());
|
||||
if (counter == null)
|
||||
{
|
||||
counter = new Counter();
|
||||
selfIds.put(id.stringValue(), counter);
|
||||
}
|
||||
counter.count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ((pathQuery.getPathStructuredFieldPositions().size() + pathQuery.getQNameStructuredFieldPositions().size()) == 0) // optimize
|
||||
// zero-term
|
||||
// case
|
||||
return null;
|
||||
|
||||
|
||||
for (StructuredFieldPosition sfp : pathQuery.getPathStructuredFieldPositions())
|
||||
{
|
||||
if (sfp.getTermText() != null)
|
||||
{
|
||||
TermPositions p = reader.termPositions(new Term(pathQuery.getPathField(), sfp.getTermText()));
|
||||
if (p == null)
|
||||
return null;
|
||||
CachingTermPositions ctp = new CachingTermPositions(p);
|
||||
sfp.setCachingTermPositions(ctp);
|
||||
}
|
||||
}
|
||||
|
||||
for (StructuredFieldPosition sfp : pathQuery.getQNameStructuredFieldPositions())
|
||||
{
|
||||
if (sfp.getTermText() != null)
|
||||
{
|
||||
TermPositions p = reader.termPositions(new Term(pathQuery.getQnameField(), sfp.getTermText()));
|
||||
if (p == null)
|
||||
return null;
|
||||
CachingTermPositions ctp = new CachingTermPositions(p);
|
||||
sfp.setCachingTermPositions(ctp);
|
||||
}
|
||||
}
|
||||
|
||||
TermPositions rootContainerPositions = null;
|
||||
if (pathQuery.getPathRootTerm() != null)
|
||||
{
|
||||
rootContainerPositions = reader.termPositions(pathQuery.getPathRootTerm());
|
||||
}
|
||||
|
||||
TermPositions rootLeafPositions = null;
|
||||
if (pathQuery.getQNameRootTerm() != null)
|
||||
{
|
||||
rootLeafPositions = reader.termPositions(pathQuery.getQNameRootTerm());
|
||||
}
|
||||
|
||||
|
||||
TermPositions tp = reader.termPositions();
|
||||
|
||||
ContainerScorer cs = null;
|
||||
|
||||
TermPositions level0 = null;
|
||||
|
||||
TermPositions nodePositions = reader.termPositions(new Term("ISNODE", "T"));
|
||||
|
||||
// StructuredFieldPosition[] test =
|
||||
// (StructuredFieldPosition[])structuredFieldPositions.toArray(new
|
||||
// StructuredFieldPosition[]{});
|
||||
if (pathQuery.getPathStructuredFieldPositions().size() > 0)
|
||||
{
|
||||
TermPositions containerPositions = reader.termPositions(new Term("ISCONTAINER", "T"));
|
||||
cs = new ContainerScorer(weight, rootContainerPositions, (StructuredFieldPosition[]) pathQuery.getPathStructuredFieldPositions().toArray(new StructuredFieldPosition[] {}),
|
||||
containerPositions, similarity, reader.norms(pathQuery.getPathField()));
|
||||
}
|
||||
else
|
||||
{
|
||||
level0 = reader.termPositions(new Term("ISROOT", "T"));
|
||||
}
|
||||
|
||||
LeafScorer ls = new LeafScorer(weight, rootLeafPositions, level0, cs, (StructuredFieldPosition[]) pathQuery.getQNameStructuredFieldPositions().toArray(new StructuredFieldPosition[] {}), nodePositions,
|
||||
selfIds, reader, similarity, reader.norms(pathQuery.getQnameField()), dictionarySertvice, repeat, tp);
|
||||
|
||||
return new PathScorer(similarity, ls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean next() throws IOException
|
||||
{
|
||||
return scorer.next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int doc()
|
||||
{
|
||||
return scorer.doc();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float score() throws IOException
|
||||
{
|
||||
return scorer.score();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean skipTo(int position) throws IOException
|
||||
{
|
||||
return scorer.skipTo(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Explanation explain(int position) throws IOException
|
||||
{
|
||||
return scorer.explain(position);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.query;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Search for a term relative to the last one found.
|
||||
*
|
||||
* @author andyh
|
||||
*/
|
||||
public class RelativeStructuredFieldPosition extends AbstractStructuredFieldPosition
|
||||
{
|
||||
|
||||
int relativePosition;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public RelativeStructuredFieldPosition(String termText)
|
||||
{
|
||||
super(termText.equals("*") ? null : termText, true, false);
|
||||
relativePosition = 1;
|
||||
|
||||
}
|
||||
|
||||
public RelativeStructuredFieldPosition()
|
||||
{
|
||||
super(null, false, false);
|
||||
relativePosition = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.alfresco.lucene.extensions.StructuredFieldPosition#matches(int,
|
||||
* int, org.apache.lucene.index.TermPositions)
|
||||
*/
|
||||
public int matches(int start, int end, int offset) throws IOException
|
||||
{
|
||||
|
||||
if (getCachingTermPositions() != null)
|
||||
{
|
||||
// Doing "termText"
|
||||
getCachingTermPositions().reset();
|
||||
int count = getCachingTermPositions().freq();
|
||||
int requiredPosition = offset + relativePosition;
|
||||
int realPosition = 0;
|
||||
int adjustedPosition = 0;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
realPosition = getCachingTermPositions().nextPosition();
|
||||
adjustedPosition = realPosition - start;
|
||||
if ((end != -1) && (realPosition > end))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (adjustedPosition == requiredPosition)
|
||||
{
|
||||
return adjustedPosition;
|
||||
}
|
||||
if (adjustedPosition > requiredPosition)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Doing "*";
|
||||
return offset + 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public String getDescription()
|
||||
{
|
||||
return "Relative Named child";
|
||||
}
|
||||
}
|
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.query;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class SelfAxisStructuredFieldPosition extends AbstractStructuredFieldPosition
|
||||
{
|
||||
|
||||
public SelfAxisStructuredFieldPosition()
|
||||
{
|
||||
super(null, true, false);
|
||||
}
|
||||
|
||||
public int matches(int start, int end, int offset) throws IOException
|
||||
{
|
||||
return offset;
|
||||
}
|
||||
|
||||
public String getDescription()
|
||||
{
|
||||
return "Self Axis";
|
||||
}
|
||||
|
||||
public boolean linkSelf()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isTerminal()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.query;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Elements used to test agains path and Qname
|
||||
*
|
||||
* @author andyh
|
||||
*/
|
||||
public interface StructuredFieldPosition
|
||||
{
|
||||
|
||||
/**
|
||||
* Does this element match
|
||||
*
|
||||
* @param start -
|
||||
* the start postion of the paths terms
|
||||
* @param end -
|
||||
* the end position of the paths terms
|
||||
* @param offset -
|
||||
* the current offset in the path
|
||||
* @return returns the next match position (usually offset + 1) or -1 if it
|
||||
* does not match.
|
||||
* @throws IOException
|
||||
*/
|
||||
public int matches(int start, int end, int offset) throws IOException;
|
||||
|
||||
/**
|
||||
* If this position is last in the chain and it is terminal it will ensure
|
||||
* it is an exact match for the length of the chain found. If false, it will
|
||||
* effectively allow prefix mathces for the likes of descendant-and-below
|
||||
* style queries.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isTerminal();
|
||||
|
||||
/**
|
||||
* Is this an absolute element; that is, it knows its exact position.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isAbsolute();
|
||||
|
||||
/**
|
||||
* This element only knows its position relative to the previous element.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isRelative();
|
||||
|
||||
/**
|
||||
* Get the test to search for in the term query. This may be null if it
|
||||
* should not have a term query
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getTermText();
|
||||
|
||||
/**
|
||||
* If absolute return the position. If relative we could compute the
|
||||
* position knowing the previous term unless this element is preceded by a
|
||||
* descendat and below style element
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getPosition();
|
||||
|
||||
/**
|
||||
* A reference to the caching term positions this element uses. This may be
|
||||
* null which indicates all terms match, in that case there is no action
|
||||
* against the index
|
||||
*
|
||||
* @param tps
|
||||
*/
|
||||
public void setCachingTermPositions(CachingTermPositions tps);
|
||||
|
||||
public CachingTermPositions getCachingTermPositions();
|
||||
|
||||
/**
|
||||
* Normally paths would require onlt parent chaining. for some it is parent
|
||||
* and child chaining.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
|
||||
public boolean linkSelf();
|
||||
|
||||
public boolean linkParent();
|
||||
|
||||
public boolean allowslinkingByParent();
|
||||
|
||||
public boolean allowsLinkingBySelf();
|
||||
|
||||
public boolean isDescendant();
|
||||
|
||||
public boolean matchesAll();
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.repo.search.impl.lucene.query;
|
||||
|
||||
import org.apache.lucene.index.Term;
|
||||
|
||||
/**
|
||||
* @author andyh
|
||||
*/
|
||||
public class StructuredFieldTerm
|
||||
{
|
||||
|
||||
private Term term;
|
||||
|
||||
private StructuredFieldPosition sfp;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public StructuredFieldTerm(Term term, StructuredFieldPosition sfp)
|
||||
{
|
||||
this.term = term;
|
||||
this.sfp = sfp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the sfp.
|
||||
*/
|
||||
public StructuredFieldPosition getSfp()
|
||||
{
|
||||
return sfp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the term.
|
||||
*/
|
||||
public Term getTerm()
|
||||
{
|
||||
return term;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user