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:
Derek Hulley
2005-12-08 07:13:07 +00:00
commit e1e6508fec
1095 changed files with 230566 additions and 0 deletions

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View 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;
}
}
}

View 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();
}

View File

@@ -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();
}
}
}

View File

@@ -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]");
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}

View File

@@ -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;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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());
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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());
}
}

View File

@@ -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);
}

View File

@@ -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);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -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>

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

File diff suppressed because it is too large Load Diff

View 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 ::= ["+", "-"] [&lt;TERM&gt; ":"] ( &lt;TERM&gt; | "(" 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;
}
}

View File

@@ -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

View 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();
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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));
}
}

View File

@@ -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);
}
}

View File

@@ -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());
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}

View File

@@ -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");
}
}

View File

@@ -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";
}
}

View File

@@ -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;
}
}

View File

@@ -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";
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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";
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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;
}
}