Author: catholicon
Date: Fri Jan 19 13:47:30 2018
New Revision: 1821652
URL: http://svn.apache.org/viewvc?rev=1821652&view=rev
Log:
OAK-7151: Support indexed based excerpts on properties
Implement SQL2 syntax and facet evaluation for facet for properties
Added:
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/ExcerptTest.java
(with props)
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java?rev=1821652&r1=1821651&r2=1821652&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java
(original)
+++
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/ast/SelectorImpl.java
Fri Jan 19 13:47:30 2018
@@ -389,8 +389,10 @@ public class SelectorImpl extends Source
for (ColumnImpl c : query.getColumns()) {
if (c.getSelector().equals(this)) {
String columnName = c.getColumnName();
- if (columnName.equals(QueryConstants.REP_EXCERPT) ||
columnName.equals(QueryConstants.OAK_SCORE_EXPLANATION)) {
+ if (columnName.equals(QueryConstants.OAK_SCORE_EXPLANATION)) {
f.restrictProperty(columnName, Operator.NOT_EQUAL, null);
+ } else if (columnName.startsWith(QueryConstants.REP_EXCERPT)) {
+ f.restrictProperty(QueryConstants.REP_EXCERPT,
Operator.EQUAL, PropertyValues.newString(columnName));
} else if (columnName.startsWith(QueryConstants.REP_FACET)) {
f.restrictProperty(QueryConstants.REP_FACET,
Operator.EQUAL, PropertyValues.newString(columnName));
}
@@ -638,6 +640,7 @@ public class SelectorImpl extends Source
return PropertyValues.create(s);
}
boolean relative =
!oakPropertyName.startsWith(QueryConstants.REP_FACET + "(")
+ && !oakPropertyName.startsWith(QueryConstants.REP_EXCERPT +
"(")
&& oakPropertyName.indexOf('/') >= 0;
Tree t = currentTree();
if (relative) {
@@ -673,8 +676,9 @@ public class SelectorImpl extends Source
result = PropertyValues.newString(local);
} else if (oakPropertyName.equals(QueryConstants.JCR_SCORE)) {
result = currentRow.getValue(QueryConstants.JCR_SCORE);
- } else if (oakPropertyName.equals(QueryConstants.REP_EXCERPT)) {
- result = currentRow.getValue(QueryConstants.REP_EXCERPT);
+ } else if (oakPropertyName.equals(QueryConstants.REP_EXCERPT)
+ || oakPropertyName.startsWith(QueryConstants.REP_EXCERPT +
"(")) {
+ result = currentRow.getValue(oakPropertyName);
} else if
(oakPropertyName.equals(QueryConstants.OAK_SCORE_EXPLANATION)) {
result = currentRow.getValue(QueryConstants.OAK_SCORE_EXPLANATION);
} else if (oakPropertyName.equals(QueryConstants.REP_SPELLCHECK)) {
Modified:
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java?rev=1821652&r1=1821651&r2=1821652&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java
(original)
+++
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LuceneIndexConstants.java
Fri Jan 19 13:47:30 2018
@@ -141,6 +141,8 @@ public interface LuceneIndexConstants {
String PROP_USE_IN_EXCERPT = "useInExcerpt";
+ String EXCERPT_NODE_FIELD_NAME = ".";
+
String PROP_NODE_SCOPE_INDEX = "nodeScopeIndex";
String PROP_PROPERTY_INDEX = "propertyIndex";
Modified:
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java?rev=1821652&r1=1821651&r2=1821652&view=diff
==============================================================================
---
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java
(original)
+++
jackrabbit/oak/trunk/oak-lucene/src/main/java/org/apache/jackrabbit/oak/plugins/index/lucene/LucenePropertyIndex.java
Fri Jan 19 13:47:30 2018
@@ -33,12 +33,15 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
+import com.google.common.base.Joiner;
import com.google.common.collect.AbstractIterator;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
import com.google.common.collect.Queues;
import com.google.common.collect.Sets;
import com.google.common.primitives.Chars;
@@ -138,11 +141,13 @@ import static org.apache.jackrabbit.oak.
import static org.apache.jackrabbit.oak.commons.PathUtils.getParentPath;
import static
org.apache.jackrabbit.oak.plugins.index.lucene.FieldNames.ANALYZED_FIELD_PREFIX;
import static
org.apache.jackrabbit.oak.plugins.index.lucene.IndexDefinition.NATIVE_SORT_ORDER;
+import static
org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.EXCERPT_NODE_FIELD_NAME;
import static
org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants.VERSION;
import static
org.apache.jackrabbit.oak.plugins.index.lucene.TermFactory.newAncestorTerm;
import static
org.apache.jackrabbit.oak.plugins.index.lucene.TermFactory.newPathTerm;
import static org.apache.jackrabbit.oak.plugins.memory.PropertyValues.newName;
import static org.apache.jackrabbit.oak.spi.query.QueryConstants.JCR_PATH;
+import static org.apache.jackrabbit.oak.spi.query.QueryConstants.REP_EXCERPT;
import static
org.apache.jackrabbit.oak.spi.query.QueryIndex.AdvancedQueryIndex;
import static org.apache.jackrabbit.oak.spi.query.QueryIndex.NativeQueryIndex;
import static org.apache.lucene.search.BooleanClause.Occur.*;
@@ -350,7 +355,7 @@ public class LucenePropertyIndex impleme
return endOfData();
}
- private LuceneResultRow convertToRow(ScoreDoc doc, IndexSearcher
searcher, String excerpt, Facets facets,
+ private LuceneResultRow convertToRow(ScoreDoc doc, IndexSearcher
searcher, Map<String, String> excerpts, Facets facets,
String explanation) throws
IOException {
IndexReader reader = searcher.getIndexReader();
//TODO Look into usage of field cache for retrieving the path
@@ -381,7 +386,7 @@ public class LucenePropertyIndex impleme
boolean shouldIncludeForHierarchy = shouldInclude(path,
plan);
LOG.trace("Matched path {}; shouldIncludeForHierarchy:
{}", path, shouldIncludeForHierarchy);
- return shouldIncludeForHierarchy? new
LuceneResultRow(path, doc.score, excerpt, facets, explanation)
+ return shouldIncludeForHierarchy? new
LuceneResultRow(path, doc.score, excerpts, facets, explanation)
: null;
}
return null;
@@ -438,10 +443,16 @@ public class LucenePropertyIndex impleme
Facets facets = FacetHelper.getFacets(searcher,
query, docs, plan, indexNode.getDefinition().isSecureFacets());
PERF_LOGGER.end(f, -1, "facets retrieved");
- PropertyRestriction restriction =
filter.getPropertyRestriction(QueryConstants.REP_EXCERPT);
- boolean addExcerpt = restriction != null &&
restriction.isNotNullRestriction();
+ Set<String> excerptFields = Sets.newHashSet();
+ for (PropertyRestriction pr :
filter.getPropertyRestrictions()) {
+ if
(QueryConstants.REP_EXCERPT.equals(pr.propertyName)) {
+ String value =
pr.first.getValue(Type.STRING);
+ excerptFields.add(value);
+ }
+ }
+ boolean addExcerpt = excerptFields.size() > 0;
- restriction =
filter.getPropertyRestriction(QueryConstants.OAK_SCORE_EXPLANATION);
+ PropertyRestriction restriction =
filter.getPropertyRestriction(QueryConstants.OAK_SCORE_EXPLANATION);
boolean addExplain = restriction != null &&
restriction.isNotNullRestriction();
Analyzer analyzer =
indexNode.getDefinition().getAnalyzer();
@@ -456,9 +467,9 @@ public class LucenePropertyIndex impleme
}
for (ScoreDoc doc : docs.scoreDocs) {
- String excerpt = null;
+ Map<String, String> excerpts = null;
if (addExcerpt) {
- excerpt = getExcerpt(query, analyzer,
searcher, doc, mergedFieldInfos);
+ excerpts = getExcerpt(query,
excerptFields, analyzer, searcher, doc, mergedFieldInfos);
}
String explanation = null;
@@ -466,7 +477,7 @@ public class LucenePropertyIndex impleme
explanation = searcher.explain(query,
doc.doc).toString();
}
- LuceneResultRow row = convertToRow(doc,
searcher, excerpt, facets, explanation);
+ LuceneResultRow row = convertToRow(doc,
searcher, excerpts, facets, explanation);
if (row != null) {
queue.add(row);
}
@@ -662,9 +673,32 @@ public class LucenePropertyIndex impleme
return include;
}
- private String getExcerpt(Query query, Analyzer analyzer, IndexSearcher
searcher, ScoreDoc doc,
- FieldInfos fieldInfos) throws IOException {
- StringBuilder excerpt = new StringBuilder();
+ private Map<String, String> getExcerpt(Query query, Set<String>
excerptFields,
+ Analyzer analyzer, IndexSearcher searcher,
ScoreDoc doc, FieldInfos fieldInfos)
+ throws IOException {
+ Set<String> excerptFieldNames = Sets.newHashSet();
+ Map<String, String> fieldNameToColumnNameMap = Maps.newHashMap();
+ Map<String, String> columnNameToExcerpts = Maps.newHashMap();
+ Set<String> nodeExcerptColumns = Sets.newHashSet();
+
+ excerptFields.forEach(columnName -> {
+ String fieldName;
+ if (REP_EXCERPT.equals(columnName)) {
+ fieldName = EXCERPT_NODE_FIELD_NAME;
+ } else {
+ fieldName = columnName.substring(REP_EXCERPT.length() + 1,
columnName.length() - 1);
+ }
+
+ if (!EXCERPT_NODE_FIELD_NAME.equals(fieldName)) {
+ excerptFieldNames.add(fieldName);
+ fieldNameToColumnNameMap.put(fieldName, columnName);
+ } else {
+ nodeExcerptColumns.add(columnName);
+ }
+ });
+
+ final boolean requireNodeLevelExcerpt = nodeExcerptColumns.size() > 0;
+
int docID = doc.doc;
List<String> names = new LinkedList<String>();
@@ -676,6 +710,10 @@ public class LucenePropertyIndex impleme
}
}
+ if (!requireNodeLevelExcerpt) {
+ names.retainAll(excerptFieldNames);
+ }
+
if (names.size() > 0) {
int[] maxPassages = new int[names.size()];
for (int i = 0; i < maxPassages.length; i++) {
@@ -687,10 +725,10 @@ public class LucenePropertyIndex impleme
for (Map.Entry<String, String[]> entry : stringMap.entrySet())
{
String value = Arrays.toString(entry.getValue());
if (value.contains("<b>")) {
- if (excerpt.length() > 0) {
- excerpt.append("...");
- }
- excerpt.append(value);
+ String fieldName = entry.getKey();
+ String columnName =
fieldNameToColumnNameMap.get(fieldName);
+
+ columnNameToExcerpts.put(columnName, value);
}
}
} catch (Exception e) {
@@ -699,8 +737,7 @@ public class LucenePropertyIndex impleme
}
// fallback if no excerpt could be retrieved using postings highlighter
- if (excerpt.length() == 0) {
-
+ if (columnNameToExcerpts.size() == 0) {
for (IndexableField field :
searcher.getIndexReader().document(doc.doc).getFields()) {
String name = field.name();
// only full text or analyzed fields
@@ -712,12 +749,21 @@ public class LucenePropertyIndex impleme
TextFragment[] textFragments =
highlighter.getBestTextFragments(tokenStream, text, true, 1);
if (textFragments != null && textFragments.length > 0)
{
for (TextFragment fragment : textFragments) {
- if (excerpt.length() > 0) {
- excerpt.append("...");
+ String columnName = null;
+ if
(name.startsWith(FieldNames.ANALYZED_FIELD_PREFIX)) {
+ columnName =
fieldNameToColumnNameMap.get(name.substring(FieldNames.ANALYZED_FIELD_PREFIX.length()));
}
- excerpt.append(fragment.toString());
+ if (columnName == null &&
requireNodeLevelExcerpt) {
+ columnName = name;
+ }
+
+ if (columnName != null) {
+ columnNameToExcerpts.put(columnName,
fragment.toString());
+ }
+ }
+ if (excerptFieldNames.size() == 0) {
+ break;
}
- break;
}
} catch (InvalidTokenOffsetsException e) {
LOG.error("higlighting failed", e);
@@ -725,7 +771,18 @@ public class LucenePropertyIndex impleme
}
}
}
- return excerpt.toString();
+
+ if (requireNodeLevelExcerpt) {
+ String nodeExcerpt =
Joiner.on("...").join(columnNameToExcerpts.values());
+
+ nodeExcerptColumns.forEach( nodeExcerptColumnName -> {
+ columnNameToExcerpts.put(nodeExcerptColumnName, nodeExcerpt);
+ });
+ }
+
+ columnNameToExcerpts.keySet().retainAll(excerptFields);
+
+ return columnNameToExcerpts;
}
@Override
@@ -1610,13 +1667,13 @@ public class LucenePropertyIndex impleme
final double score;
final String suggestion;
final boolean isVirutal;
- final String excerpt;
+ final Map<String, String> excerpts;
final String explanation;
final Facets facets;
- LuceneResultRow(String path, double score, String excerpt, Facets
facets, String explanation) {
+ LuceneResultRow(String path, double score, Map<String, String>
excerpts, Facets facets, String explanation) {
this.explanation = explanation;
- this.excerpt = excerpt;
+ this.excerpts = excerpts;
this.facets = facets;
this.isVirutal = false;
this.path = path;
@@ -1629,7 +1686,7 @@ public class LucenePropertyIndex impleme
this.path = "/";
this.score = weight;
this.suggestion = suggestion;
- this.excerpt = null;
+ this.excerpts = null;
this.facets = null;
this.explanation = null;
}
@@ -1741,8 +1798,11 @@ public class LucenePropertyIndex impleme
if
(QueryConstants.OAK_SCORE_EXPLANATION.equals(columnName)) {
return
PropertyValues.newString(currentRow.explanation);
}
- if (QueryConstants.REP_EXCERPT.equals(columnName)) {
- return PropertyValues.newString(currentRow.excerpt);
+ if (columnName.startsWith(QueryConstants.REP_EXCERPT)) {
+ String excerpt = currentRow.excerpts.get(columnName);
+ if (excerpt != null) {
+ return PropertyValues.newString(excerpt);
+ }
}
if (columnName.startsWith(QueryConstants.REP_FACET)) {
String facetFieldName =
FacetHelper.parseFacetField(columnName);
Added:
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/ExcerptTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/ExcerptTest.java?rev=1821652&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/ExcerptTest.java
(added)
+++
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/ExcerptTest.java
Fri Jan 19 13:47:30 2018
@@ -0,0 +1,310 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.jackrabbit.oak.plugins.index.lucene;
+
+import com.google.common.base.Joiner;
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.oak.InitialContent;
+import org.apache.jackrabbit.oak.Oak;
+import org.apache.jackrabbit.oak.api.Blob;
+import org.apache.jackrabbit.oak.api.ContentRepository;
+import org.apache.jackrabbit.oak.api.PropertyValue;
+import org.apache.jackrabbit.oak.api.Result;
+import org.apache.jackrabbit.oak.api.ResultRow;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.plugins.index.nodetype.NodeTypeIndexProvider;
+import
org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexEditorProvider;
+import org.apache.jackrabbit.oak.plugins.memory.ArrayBasedBlob;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
+import org.apache.jackrabbit.oak.query.AbstractQueryTest;
+import org.apache.jackrabbit.oak.spi.commit.Observer;
+import org.apache.jackrabbit.oak.spi.query.QueryIndexProvider;
+import org.apache.jackrabbit.oak.spi.security.OpenSecurityProvider;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.apache.jackrabbit.oak.api.QueryEngine.NO_BINDINGS;
+import static org.apache.jackrabbit.oak.api.Type.STRING;
+import static
org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NAME;
+import static
org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NODE_TYPE;
+import static
org.apache.jackrabbit.oak.plugins.index.IndexConstants.REINDEX_PROPERTY_NAME;
+import static
org.apache.jackrabbit.oak.plugins.index.IndexConstants.TYPE_PROPERTY_NAME;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+public class ExcerptTest extends AbstractQueryTest {
+
+ @Before
+ public void setup() throws Exception { //named so that it gets called
after super.before :-/
+ Tree rootTree = root.getTree("/");
+
+ Tree def =
rootTree.addChild(INDEX_DEFINITIONS_NAME).addChild("testExcerpt");
+ def.setProperty(JcrConstants.JCR_PRIMARYTYPE,
INDEX_DEFINITIONS_NODE_TYPE, Type.NAME);
+ def.setProperty(TYPE_PROPERTY_NAME, LuceneIndexConstants.TYPE_LUCENE);
+ def.setProperty(REINDEX_PROPERTY_NAME, true);
+ def.setProperty(LuceneIndexConstants.EVALUATE_PATH_RESTRICTION, true);
+ def.setProperty(LuceneIndexConstants.COMPAT_MODE,
IndexFormatVersion.V2.getVersion());
+
+ Tree properties = def.addChild(LuceneIndexConstants.INDEX_RULES)
+ .addChild("nt:base")
+ .addChild(LuceneIndexConstants.PROP_NODE);
+
+ Tree notIndexedProp = properties.addChild("baz");
+ notIndexedProp.setProperty(LuceneIndexConstants.PROP_NODE_SCOPE_INDEX,
true);
+
+ Tree relativeProp = properties.addChild("relative-baz");
+ relativeProp.setProperty(LuceneIndexConstants.PROP_ANALYZED, true);
+ relativeProp.setProperty(LuceneIndexConstants.PROP_USE_IN_EXCERPT,
true);
+ relativeProp.setProperty(LuceneIndexConstants.PROP_NAME,
"relative/baz");
+
+ Tree allProps = properties.addChild("allProps");
+ allProps.setProperty(LuceneIndexConstants.PROP_ANALYZED, true);
+ allProps.setProperty(LuceneIndexConstants.PROP_NODE_SCOPE_INDEX, true);
+ allProps.setProperty(LuceneIndexConstants.PROP_USE_IN_EXCERPT, true);
+ allProps.setProperty(LuceneIndexConstants.PROP_NAME,
LuceneIndexConstants.REGEX_ALL_PROPS);
+ allProps.setProperty(LuceneIndexConstants.PROP_IS_REGEX, true);
+
+ root.commit();
+ }
+
+ @Override
+ protected ContentRepository createRepository() {
+ LuceneIndexEditorProvider editorProvider = new
LuceneIndexEditorProvider();
+ LuceneIndexProvider provider = new LuceneIndexProvider();
+
+ NodeStore nodeStore = new MemoryNodeStore();
+ return new Oak(nodeStore)
+ .with(new InitialContent())
+ .with(new OpenSecurityProvider())
+ .with((Observer) provider)
+ .with(editorProvider)
+ .with((QueryIndexProvider)provider)
+ .with(new PropertyIndexEditorProvider())
+ .with(new NodeTypeIndexProvider())
+ .createContentRepository();
+ }
+
+ @Test
+ public void getAllSelectedColumns() throws Exception {
+ Tree contentRoot = root.getTree("/").addChild("testRoot");
+ contentRoot.setProperty("foo", "is fox ifoxing");
+ contentRoot.setProperty("bar", "ifoxing fox");
+ contentRoot.setProperty("baz", "fox ifoxing");
+ root.commit();
+
+ List<String> columns = newArrayList("rep:excerpt", "rep:excerpt(.)",
"rep:excerpt(foo)", "rep:excerpt(bar)");
+ String selectColumns = Joiner.on(",").join(
+ columns.stream().map(col -> "[" + col +
"]").collect(Collectors.toList())
+ );
+ String query = "SELECT " + selectColumns + " FROM [nt:base] WHERE
CONTAINS(*, 'fox')";
+ Result result = executeQuery(query, SQL2, NO_BINDINGS);
+ Iterator<? extends ResultRow> resultIter = result.getRows().iterator();
+ assertTrue(resultIter.hasNext());
+ ResultRow firstRow = resultIter.next();
+
+ PropertyValue excerptValue;
+ String excerpt;
+
+ for (String col : columns) {
+ excerptValue = firstRow.getValue(col);
+ assertNotNull(col + " not evaluated", excerptValue);
+ excerpt = excerptValue.getValue(STRING);
+ assertFalse(col + " didn't evaluate correctly - got '" + excerpt +
"'",
+ excerpt.contains("i<strong>fox</foxing>ing"));
+ }
+ }
+
+ @Test
+ public void nodeExcerpt() throws Exception {
+ Tree contentRoot = root.getTree("/").addChild("testRoot");
+ contentRoot.setProperty("foo", "is fox ifoxing");
+ contentRoot.setProperty("bar", "ifoxing fox");
+ root.commit();
+
+ String query = "SELECT [rep:excerpt],[rep:excerpt(.)] FROM [nt:base]
WHERE CONTAINS(*, 'fox')";
+ Result result = executeQuery(query, SQL2, NO_BINDINGS);
+ Iterator<? extends ResultRow> resultIter = result.getRows().iterator();
+ assertTrue(resultIter.hasNext());
+ ResultRow firstRow = resultIter.next();
+
+ PropertyValue nodeExcerpt;
+ String excerpt1, excerpt2;
+
+
+ nodeExcerpt = firstRow.getValue("rep:excerpt");
+ assertNotNull("rep:excerpt not evaluated", nodeExcerpt);
+ excerpt1 = nodeExcerpt.getValue(STRING);
+ assertTrue("rep:excerpt didn't evaluate correctly - got '" + excerpt1
+ "'",
+ "is <strong>fox</strong> ifoxing".equals(excerpt1) || "ifoxing
<strong>fox</strong>".equals(excerpt1));
+
+ nodeExcerpt = firstRow.getValue("rep:excerpt(.)");
+ assertNotNull("rep:excerpt(.) not evaluated", nodeExcerpt);
+ excerpt2 = nodeExcerpt.getValue(STRING);
+ assertEquals("excerpt extracted via rep:excerpt not same as
rep:excerpt(.)", excerpt1, excerpt2);
+ }
+
+ @Test
+ public void nonIndexedRequestedPropExcerpt() throws Exception {
+ Tree contentRoot = root.getTree("/").addChild("testRoot");
+ contentRoot.setProperty("foo", "fox");
+ contentRoot.setProperty("baz", "is fox ifoxing");
+ root.commit();
+
+ String query = "SELECT [rep:excerpt(baz)] FROM [nt:base] WHERE
CONTAINS(*, 'fox')";
+ Result result = executeQuery(query, SQL2, NO_BINDINGS);
+ Iterator<? extends ResultRow> resultIter = result.getRows().iterator();
+ assertTrue(resultIter.hasNext());
+ ResultRow firstRow = resultIter.next();
+
+ PropertyValue nodeExcerpt = firstRow.getValue("rep:excerpt(baz)");
+ assertNull("rep:excerpt(baz) if requested explicitly must be indexed
to be evaluated", nodeExcerpt);
+ }
+
+ @Test
+ public void propExcerpt() throws Exception {
+ Tree contentRoot = root.getTree("/").addChild("testRoot");
+ contentRoot.setProperty("foo", "is fox ifoxing");
+ root.commit();
+
+ String query = "SELECT [rep:excerpt(foo)] FROM [nt:base] WHERE
CONTAINS(*, 'fox')";
+ Result result = executeQuery(query, SQL2, NO_BINDINGS);
+ Iterator<? extends ResultRow> resultIter = result.getRows().iterator();
+ assertTrue(resultIter.hasNext());
+ ResultRow firstRow = resultIter.next();
+
+ PropertyValue nodeExcerpt;
+ String excerpt;
+
+ nodeExcerpt = firstRow.getValue("rep:excerpt(foo)");
+ assertNotNull("rep:excerpt(foo) not evaluated", nodeExcerpt);
+ excerpt = nodeExcerpt.getValue(STRING);
+ assertTrue("rep:excerpt(foo) didn't evaluate correctly - got '" +
excerpt + "'",
+ "is <strong>fox</strong> ifoxing".equals(excerpt));
+ }
+
+ @Test
+ public void indexedNonRequestedPropExcerpt() throws Exception {
+ Tree contentRoot = root.getTree("/").addChild("testRoot");
+ contentRoot.setProperty("foo", "is fox ifoxing");
+ root.commit();
+
+ String query = "SELECT [rep:excerpt] FROM [nt:base] WHERE CONTAINS(*,
'fox')";
+ Result result = executeQuery(query, SQL2, NO_BINDINGS);
+ Iterator<? extends ResultRow> resultIter = result.getRows().iterator();
+ assertTrue(resultIter.hasNext());
+ ResultRow firstRow = resultIter.next();
+
+ PropertyValue nodeExcerpt;
+ String excerpt;
+
+ nodeExcerpt = firstRow.getValue("rep:excerpt(foo)");
+ assertNotNull("rep:excerpt(foo) not evaluated", nodeExcerpt);
+ excerpt = nodeExcerpt.getValue(STRING);
+ assertTrue("rep:excerpt(foo) didn't evaluate correctly - got '" +
excerpt + "'",
+ excerpt.contains("<strong>fox</strong>"));
+
+ assertTrue("rep:excerpt(foo) highlighting inside words - got '" +
excerpt + "'",
+ !excerpt.contains("i<strong>fox</strong>ing"));
+ }
+
+ @Test
+ public void nonIndexedNonRequestedPropExcerpt() throws Exception {
+ Tree contentRoot = root.getTree("/").addChild("testRoot");
+ contentRoot.setProperty("foo", "fox");
+ contentRoot.setProperty("baz", "is fox ifoxing");
+ root.commit();
+
+ String query = "SELECT [rep:excerpt] FROM [nt:base] WHERE CONTAINS(*,
'fox')";
+ Result result = executeQuery(query, SQL2, NO_BINDINGS);
+ Iterator<? extends ResultRow> resultIter = result.getRows().iterator();
+ assertTrue(resultIter.hasNext());
+ ResultRow firstRow = resultIter.next();
+
+ PropertyValue nodeExcerpt;
+ String excerpt;
+
+ nodeExcerpt = firstRow.getValue("rep:excerpt(baz)");
+ assertNotNull("rep:excerpt(baz) not evaluated", nodeExcerpt);
+ excerpt = nodeExcerpt.getValue(STRING);
+ assertTrue("rep:excerpt(foo) didn't evaluate correctly - got '" +
excerpt + "'",
+ excerpt.contains("<strong>fox</strong>"));
+
+ assertTrue("rep:excerpt(baz) highlighting inside words - got '" +
excerpt + "'",
+ !excerpt.contains("i<strong>fox</strong>ing"));
+ }
+
+ @Test
+ public void relativePropExcerpt() throws Exception {
+ Tree contentRoot = root.getTree("/").addChild("testRoot");
+ contentRoot.addChild("relative").setProperty("baz", "is fox ifoxing");
+ root.commit();
+
+ String query = "SELECT [rep:excerpt(relative/baz)] FROM [nt:base]
WHERE CONTAINS([relative/baz], 'fox')";
+ Result result = executeQuery(query, SQL2, NO_BINDINGS);
+ Iterator<? extends ResultRow> resultIter = result.getRows().iterator();
+ assertTrue(resultIter.hasNext());
+ ResultRow firstRow = resultIter.next();
+
+ PropertyValue nodeExcerpt;
+ String excerpt;
+
+ nodeExcerpt = firstRow.getValue("rep:excerpt(relative/baz)");
+ assertNotNull("rep:excerpt(relative/baz) not evaluated", nodeExcerpt);
+ excerpt = nodeExcerpt.getValue(STRING);
+ assertTrue("rep:excerpt(relative/baz) didn't evaluate correctly - got
'" + excerpt + "'",
+ "is <strong>fox</strong> ifoxing".equals(excerpt));
+ }
+
+ @Test
+ public void binaryExcerpt() throws Exception {
+ Tree contentRoot = root.getTree("/").addChild("testRoot");
+
+ String binaryText = "is fox foxing as a fox cub";
+ Blob blob = new ArrayBasedBlob(binaryText.getBytes());
+ TestUtil.createFileNode(contentRoot, "binaryNode", blob, "text/plain");
+ root.commit();
+
+ String query = "SELECT [rep:excerpt] FROM [nt:base] WHERE CONTAINS(*,
'fox')";
+ Result result = executeQuery(query, SQL2, NO_BINDINGS);
+ Iterator<? extends ResultRow> resultIter = result.getRows().iterator();
+ assertTrue(resultIter.hasNext());
+ ResultRow firstRow = resultIter.next();
+
+ PropertyValue nodeExcerpt;
+ String excerpt;
+
+ nodeExcerpt = firstRow.getValue("rep:excerpt");
+ assertNotNull("rep:excerpt not evaluated", nodeExcerpt);
+ excerpt = nodeExcerpt.getValue(STRING);
+ String expected = binaryText.replaceAll(" fox ", "
<strong>fox</strong> ");
+ assertTrue("rep:excerpt didn't evaluate correctly - got '" + excerpt +
"'",
+ excerpt.contains(expected));
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/ExcerptTest.java
------------------------------------------------------------------------------
svn:eol-style = native