You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by mk...@apache.org on 2020/08/12 13:46:45 UTC
svn commit: r1880807 [3/4] - in /jackrabbit/oak/trunk:
oak-jcr/src/test/java/org/apache/jackrabbit/oak/query/
oak-lucene/src/test/java/org/apache/jackrabbit/oak/plugins/index/lucene/
oak-search-elastic/ oak-search-elastic/src/test/java/org/apache/jackr...
Added: jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexAggregationCommonTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexAggregationCommonTest.java?rev=1880807&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexAggregationCommonTest.java (added)
+++ jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexAggregationCommonTest.java Wed Aug 12 13:46:45 2020
@@ -0,0 +1,430 @@
+/*
+ * 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;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants;
+import org.apache.jackrabbit.oak.query.AbstractQueryTest;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+
+import static com.google.common.collect.ImmutableList.of;
+import static com.google.common.collect.Lists.newArrayList;
+import static org.apache.jackrabbit.JcrConstants.JCR_CONTENT;
+import static org.apache.jackrabbit.JcrConstants.JCR_DATA;
+import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
+import static org.apache.jackrabbit.JcrConstants.NT_FILE;
+import static org.apache.jackrabbit.JcrConstants.NT_FOLDER;
+import static org.apache.jackrabbit.oak.plugins.memory.BinaryPropertyState.binaryProperty;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public abstract class IndexAggregationCommonTest extends AbstractQueryTest {
+ protected IndexOptions indexOptions;
+
+ @Override
+ protected void createTestIndexNode() throws Exception {
+ Tree index = root.getTree("/");
+ Tree indexDefn = createTestIndexNode(index, indexOptions.getIndexType());
+ TestUtil.useV2(indexDefn);
+ //Aggregates
+ TestUtil.newNodeAggregator(indexDefn)
+ .newRuleWithName(NT_FILE, newArrayList(JCR_CONTENT, JCR_CONTENT + "/*"))
+ .newRuleWithName(NT_FOLDER, newArrayList("myFile", "subfolder/subsubfolder/file"));
+
+ //Include all properties
+ Tree props = TestUtil.newRulePropTree(indexDefn, "nt:base");
+ TestUtil.enableForFullText(props, FulltextIndexConstants.REGEX_ALL_PROPS, true);
+
+ root.commit();
+ }
+
+ /**
+ * simple index aggregation from jcr:content to nt:file
+ */
+ @Test
+ public void testNtFileAggregate() throws Exception {
+
+ String sqlBase = "SELECT * FROM [nt:file] as f WHERE";
+ String sqlCat = sqlBase + " CONTAINS (f.*, 'cat')";
+ String sqlDog = sqlBase + " CONTAINS (f.*, 'dog')";
+
+ Tree file = root.getTree("/").addChild("myFile");
+ file.setProperty(JCR_PRIMARYTYPE, NT_FILE, Type.NAME);
+ Tree resource = file.addChild(JCR_CONTENT);
+ resource.setProperty(JCR_PRIMARYTYPE, "nt:resource", Type.NAME);
+ resource.setProperty("jcr:lastModified", Calendar.getInstance());
+ resource.setProperty("jcr:encoding", "UTF-8");
+ resource.setProperty("jcr:mimeType", "text/plain");
+ resource.setProperty(binaryProperty(JCR_DATA,
+ "the quick brown fox jumps over the lazy dog."));
+ root.commit();
+ assertQuery(sqlDog, ImmutableList.of("/myFile"));
+
+ // update jcr:data
+ root.getTree("/")
+ .getChild("myFile")
+ .getChild(JCR_CONTENT)
+ .setProperty(
+ binaryProperty(JCR_DATA,
+ "the quick brown fox jumps over the lazy cat."));
+ root.commit();
+ assertQuery(sqlDog, new ArrayList<String>());
+ assertQuery(sqlCat, ImmutableList.of("/myFile"));
+
+ // replace jcr:content with unstructured
+ root.getTree("/").getChild("myFile").getChild(JCR_CONTENT).remove();
+
+ Tree unstrContent = root.getTree("/").getChild("myFile")
+ .addChild(JCR_CONTENT);
+ unstrContent.setProperty(JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED,
+ Type.NAME);
+
+ Tree foo = unstrContent.addChild("foo");
+ foo.setProperty(JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED,
+ Type.NAME);
+ foo.setProperty("text", "the quick brown fox jumps over the lazy dog.");
+ root.commit();
+
+ assertQuery(sqlDog, ImmutableList.of("/myFile"));
+ assertQuery(sqlCat, new ArrayList<String>());
+
+ // remove foo
+ root.getTree("/").getChild("myFile").getChild(JCR_CONTENT)
+ .getChild("foo").remove();
+
+ root.commit();
+ assertQuery(sqlDog, new ArrayList<String>());
+ assertQuery(sqlCat, new ArrayList<String>());
+
+ // replace jcr:content again with resource
+ root.getTree("/").getChild("myFile").getChild(JCR_CONTENT).remove();
+
+ resource = root.getTree("/").getChild("myFile").addChild(JCR_CONTENT);
+ resource.setProperty(JCR_PRIMARYTYPE, "nt:resource", Type.NAME);
+ resource.setProperty("jcr:lastModified", Calendar.getInstance());
+ resource.setProperty("jcr:encoding", "UTF-8");
+ resource.setProperty("jcr:mimeType", "text/plain");
+ resource.setProperty(binaryProperty(JCR_DATA,
+ "the quick brown fox jumps over the lazy cat."));
+ root.commit();
+ assertQuery(sqlDog, new ArrayList<String>());
+ assertQuery(sqlCat, ImmutableList.of("/myFile"));
+
+ }
+
+ @Test
+ public void testChildNodeWithOr() throws Exception {
+ Tree file = root.getTree("/").addChild("myFile");
+ file.setProperty(JCR_PRIMARYTYPE, NT_FILE, Type.NAME);
+ Tree resource = file.addChild(JCR_CONTENT);
+ resource.setProperty(JCR_PRIMARYTYPE, "nt:resource", Type.NAME);
+ resource.setProperty("jcr:lastModified", Calendar.getInstance());
+ resource.setProperty("jcr:encoding", "UTF-8");
+ resource.setProperty("jcr:mimeType", "text/plain");
+ resource.setProperty(binaryProperty(JCR_DATA,
+ "the quick brown fox jumps over the lazy dog."));
+
+ resource.setProperty("jcr:title", "title");
+ resource.setProperty("jcr:description", "description");
+
+ root.commit();
+
+ String matchContentSimple = "//element(*, nt:file)[(jcr:contains(jcr:content, 'dog'))]";
+ assertQuery(matchContentSimple, "xpath", ImmutableList.of("/myFile"));
+
+ String matchContent = " //element(*, nt:file)[(jcr:contains(jcr:content, 'dog') or jcr:contains(jcr:content/@jcr:title, 'invalid') or jcr:contains(jcr:content/@jcr:description, 'invalid'))]";
+ assertQuery(matchContent, "xpath", ImmutableList.of("/myFile"));
+
+ String matchTitle = " //element(*, nt:file)[(jcr:contains(jcr:content, 'invalid') or jcr:contains(jcr:content/@jcr:title, 'title') or jcr:contains(jcr:content/@jcr:description, 'invalid'))]";
+ assertQuery(matchTitle, "xpath", ImmutableList.of("/myFile"));
+
+ String matchDesc = " //element(*, nt:file)[(jcr:contains(jcr:content, 'invalid') or jcr:contains(jcr:content/@jcr:title, 'invalid') or jcr:contains(jcr:content/@jcr:description, 'description'))]";
+ assertQuery(matchDesc, "xpath", ImmutableList.of("/myFile"));
+
+ String matchNone = " //element(*, nt:file)[(jcr:contains(jcr:content, 'invalid') or jcr:contains(jcr:content/@jcr:title, 'invalid') or jcr:contains(jcr:content/@jcr:description, 'invalid'))]";
+ assertQuery(matchNone, "xpath", new ArrayList<String>());
+ }
+
+ @Test
+ public void testChildNodeWithOrComposite() throws Exception {
+
+ Tree folder = root.getTree("/").addChild("myFolder");
+ folder.setProperty(JCR_PRIMARYTYPE, NT_FOLDER, Type.NAME);
+ Tree file = folder.addChild("myFile");
+ file.setProperty(JCR_PRIMARYTYPE, NT_FILE, Type.NAME);
+ file.setProperty("jcr:title", "title");
+ file.setProperty("jcr:description", "description");
+
+ Tree resource = file.addChild(JCR_CONTENT);
+ resource.setProperty(JCR_PRIMARYTYPE, "nt:resource", Type.NAME);
+ resource.setProperty("jcr:lastModified", Calendar.getInstance());
+ resource.setProperty("jcr:encoding", "UTF-8");
+ resource.setProperty("jcr:mimeType", "text/plain");
+ resource.setProperty(binaryProperty(JCR_DATA,
+ "the quick brown fox jumps over the lazy dog."));
+
+ root.commit();
+
+ String matchContentSimple = "//element(*, nt:folder)[(jcr:contains(myFile, 'dog'))]";
+ assertQuery(matchContentSimple, "xpath", ImmutableList.of("/myFolder"));
+
+ String matchContent = " //element(*, nt:folder)[(jcr:contains(myFile, 'dog') or jcr:contains(myFile/@jcr:title, 'invalid') or jcr:contains(myFile/@jcr:description, 'invalid'))]";
+ assertQuery(matchContent, "xpath", ImmutableList.of("/myFolder"));
+
+ String matchTitle = " //element(*, nt:folder)[(jcr:contains(myFile, 'invalid') or jcr:contains(myFile/@jcr:title, 'title') or jcr:contains(myFile/@jcr:description, 'invalid'))]";
+ assertQuery(matchTitle, "xpath", ImmutableList.of("/myFolder"));
+
+ String matchDesc = " //element(*, nt:folder)[(jcr:contains(myFile, 'invalid') or jcr:contains(myFile/@jcr:title, 'invalid') or jcr:contains(myFile/@jcr:description, 'description'))]";
+ assertQuery(matchDesc, "xpath", ImmutableList.of("/myFolder"));
+
+ String matchNone = " //element(*, nt:folder)[(jcr:contains(myFile, 'invalid') or jcr:contains(myFile/@jcr:title, 'invalid') or jcr:contains(myFile/@jcr:description, 'invalid'))]";
+ assertQuery(matchNone, "xpath", new ArrayList<String>());
+
+ String matchOnlyTitleOr = " //element(*, nt:folder)[(jcr:contains(myFile/@jcr:title, 'title') or jcr:contains(myFile/@jcr:title, 'unknown') )]";
+ assertQuery(matchOnlyTitleOr, "xpath", ImmutableList.of("/myFolder"));
+ }
+
+ @Test
+ public void testNodeTypes() throws Exception {
+
+ Tree folder = root.getTree("/").addChild("myFolder");
+ folder.setProperty(JCR_PRIMARYTYPE, NT_FOLDER, Type.NAME);
+ Tree file = folder.addChild("myFile");
+ file.setProperty(JCR_PRIMARYTYPE, NT_FILE, Type.NAME);
+ file.setProperty("jcr:title", "title");
+ file.setProperty("jcr:description", "description");
+
+ Tree resource = file.addChild(JCR_CONTENT);
+ resource.setProperty(JCR_PRIMARYTYPE, "nt:resource", Type.NAME);
+ resource.setProperty("jcr:lastModified", Calendar.getInstance());
+ resource.setProperty("jcr:encoding", "UTF-8");
+ resource.setProperty("jcr:mimeType", "text/plain");
+ resource.setProperty(binaryProperty(JCR_DATA,
+ "the quick brown fox jumps over the lazy dog."));
+
+ root.commit();
+
+ String matchContentSimple = "//*[( jcr:contains(., 'dog') and @jcr:primaryType = 'nt:file' )]";
+ assertQuery(matchContentSimple, "xpath", ImmutableList.of("/myFolder/myFile"));
+
+ String matchContentDouble = "//*[( jcr:contains(., 'dog') and (@jcr:primaryType = 'nt:file' or @jcr:primaryType = 'nt:folder') )]";
+ assertQuery(matchContentDouble, "xpath", ImmutableList.of("/myFolder", "/myFolder/myFile"));
+ }
+
+ @Test
+ public void testNodeTypesDeep() throws Exception {
+
+ Tree folder = root.getTree("/").addChild("myFolder");
+ folder.setProperty(JCR_PRIMARYTYPE, NT_FOLDER, Type.NAME);
+
+ Tree folder2 = folder.addChild("subfolder");
+ folder2.setProperty(JCR_PRIMARYTYPE, "nt:unstructured", Type.NAME);
+
+ Tree folder3 = folder2.addChild("subsubfolder");
+ folder3.setProperty(JCR_PRIMARYTYPE, "nt:unstructured", Type.NAME);
+ file(folder3, "file");
+
+ root.commit();
+
+ String xpath = "//element(*, nt:folder)[jcr:contains(., 'dog')]";
+ assertQuery(xpath, "xpath", ImmutableList.of("/myFolder"));
+ }
+
+ private static void file(Tree parent, String name) {
+ Tree file = parent.addChild(name);
+ file.setProperty(JCR_PRIMARYTYPE, NT_FILE, Type.NAME);
+
+ Tree resource = file.addChild(JCR_CONTENT);
+ resource.setProperty(JCR_PRIMARYTYPE, "nt:resource", Type.NAME);
+ resource.setProperty("jcr:lastModified", Calendar.getInstance());
+ resource.setProperty("jcr:encoding", "UTF-8");
+ resource.setProperty("jcr:mimeType", "text/plain");
+ resource.setProperty(binaryProperty(JCR_DATA,
+ "the quick brown fox jumps over the lazy dog."));
+ }
+
+ @Test
+ public void testChildNodeProperty() throws Exception {
+ Tree file = root.getTree("/").addChild("myFile");
+ file.setProperty(JCR_PRIMARYTYPE, NT_FILE, Type.NAME);
+ Tree resource = file.addChild(JCR_CONTENT);
+ resource.setProperty(JCR_PRIMARYTYPE, "nt:resource", Type.NAME);
+ resource.setProperty("jcr:lastModified", Calendar.getInstance());
+ resource.setProperty("jcr:encoding", "UTF-8");
+ resource.setProperty("jcr:mimeType", "text/plain");
+ resource.setProperty(binaryProperty(JCR_DATA,
+ "the quick brown fox jumps over the lazy dog."));
+
+ resource.setProperty("jcr:title", "title");
+ resource.setProperty("jcr:description", "description");
+
+ root.commit();
+
+ String matchChildSimple = "//*[( jcr:contains(@jcr:title, 'title') )]";
+ assertQuery(matchChildSimple, "xpath", ImmutableList.of("/myFile/jcr:content"));
+
+ String matchChildWithStar = "//*[( jcr:contains(., 'dog') and jcr:contains(@jcr:title, 'title') )]";
+ assertQuery(matchChildWithStar, "xpath", ImmutableList.of("/myFile/jcr:content"));
+
+ }
+
+
+ @Test
+ public void testChildNodeProperty2() throws Exception {
+
+ Tree file = root.getTree("/").addChild("myFile");
+ file.setProperty(JCR_PRIMARYTYPE, NT_FILE, Type.NAME);
+ Tree resource = file.addChild(JCR_CONTENT);
+ resource.setProperty(JCR_PRIMARYTYPE, "nt:resource", Type.NAME);
+ resource.setProperty(binaryProperty(JCR_DATA,
+ "the quick brown fox jumps over the lazy dog."));
+ resource.setProperty("jcr:title", "title");
+ resource.setProperty("jcr:description", "description");
+
+ Tree file2 = root.getTree("/").addChild("myFile2");
+ file2.setProperty(JCR_PRIMARYTYPE, NT_FILE, Type.NAME);
+ Tree resource2 = file2.addChild(JCR_CONTENT);
+ resource2.setProperty(JCR_PRIMARYTYPE, "nt:resource", Type.NAME);
+ resource2.setProperty(binaryProperty(JCR_DATA,
+ "the quick brown fox jumps over the lazy dog."));
+ resource2.setProperty("jcr:title", "other");
+ resource.setProperty("jcr:description", "title");
+
+ root.commit();
+
+ String matchChildSimple = "//*[( jcr:contains(jcr:content/@jcr:title, 'title') )]";
+ assertQuery(matchChildSimple, "xpath", ImmutableList.of("/myFile"));
+
+ }
+
+ @Test
+ public void testPreventDoubleAggregation() throws Exception {
+ Tree file = root.getTree("/").addChild("myFile");
+ file.setProperty(JCR_PRIMARYTYPE, NT_FILE, Type.NAME);
+ file.setProperty("jcr:title", "fox");
+
+ Tree resource = file.addChild(JCR_CONTENT);
+ resource.setProperty(JCR_PRIMARYTYPE, "nt:resource", Type.NAME);
+ resource.setProperty("jcr:lastModified", Calendar.getInstance());
+ resource.setProperty("jcr:encoding", "UTF-8");
+ resource.setProperty("jcr:mimeType", "text/plain");
+ resource.setProperty(binaryProperty(JCR_DATA,
+ "the quick brown fox jumps over the lazy dog."));
+ root.commit();
+
+ String matchChildSimple = "//element(*, nt:file)[( jcr:contains(., 'fox') )]";
+ assertQuery(matchChildSimple, "xpath",
+ ImmutableList.of("/myFile"));
+ }
+
+ @Test
+ public void testDifferentNodes() throws Exception {
+
+ Tree folder = root.getTree("/").addChild("myFolder");
+ folder.setProperty(JCR_PRIMARYTYPE, NT_FOLDER, Type.NAME);
+ Tree file = folder.addChild("myFile");
+ file.setProperty(JCR_PRIMARYTYPE, NT_FILE, Type.NAME);
+ file.setProperty("jcr:title", "title");
+ file.setProperty("jcr:description", "description");
+
+ Tree resource = file.addChild(JCR_CONTENT);
+ resource.setProperty(JCR_PRIMARYTYPE, "nt:resource", Type.NAME);
+ resource.setProperty("jcr:lastModified", Calendar.getInstance());
+ resource.setProperty("jcr:encoding", "UTF-8");
+ resource.setProperty("jcr:mimeType", "text/plain");
+ resource.setProperty(binaryProperty(JCR_DATA,
+ "the quick brown fox jumps over the lazy dog."));
+
+ root.commit();
+
+ assertQuery(
+ "//element(*, nt:file)[jcr:contains(., 'dog')]",
+ "xpath", ImmutableList.of("/myFolder/myFile"));
+
+ assertQuery(
+ "//element(*, nt:file)[jcr:contains(., 'title')]",
+ "xpath", ImmutableList.of("/myFolder/myFile"));
+
+ assertQuery(
+ "//element(*, nt:file)[jcr:contains(., 'dog') and jcr:contains(., 'title')]",
+ "xpath", ImmutableList.of("/myFolder/myFile"));
+
+ // double aggregation dupes
+ assertQuery(
+ "//*[(jcr:contains(., 'dog') or jcr:contains(jcr:content, 'dog') )]",
+ "xpath", ImmutableList.of("/myFolder", "/myFolder/myFile", "/myFolder/myFile/jcr:content"));
+ }
+
+ @Test
+ public void oak3371AggregateV2() throws CommitFailedException {
+ oak3371();
+ }
+
+ @Test
+ public void oak3371AggregateV1() throws CommitFailedException {
+
+ Tree indexdef = root.getTree("/oak:index/" + TEST_INDEX_NAME);
+ assertNotNull(indexdef);
+ assertTrue(indexdef.exists());
+ indexdef.setProperty(FulltextIndexConstants.COMPAT_MODE, 1L);
+ indexdef.setProperty(IndexConstants.REINDEX_PROPERTY_NAME, true);
+ root.commit();
+
+ oak3371();
+ }
+
+ private void oak3371() throws CommitFailedException {
+ setTraversalEnabled(false);
+ Tree test, t;
+
+ test = root.getTree("/").addChild("test");
+ t = test.addChild("a");
+ t.setProperty(JCR_PRIMARYTYPE, NT_FOLDER, Type.NAME);
+ t.setProperty("foo", "bar");
+ t = test.addChild("b");
+ t.setProperty(JCR_PRIMARYTYPE, NT_FOLDER, Type.NAME);
+ t.setProperty("foo", "cat");
+ t = test.addChild("c");
+ t.setProperty(JCR_PRIMARYTYPE, NT_FOLDER, Type.NAME);
+ t = test.addChild("d");
+ t.setProperty(JCR_PRIMARYTYPE, NT_FOLDER, Type.NAME);
+ t.setProperty("foo", "bar cat");
+ root.commit();
+
+ assertQuery(
+ "SELECT * FROM [nt:folder] WHERE ISDESCENDANTNODE('/test') AND CONTAINS(foo, 'bar')",
+ of("/test/a", "/test/d"));
+ assertQuery(
+ "SELECT * FROM [nt:folder] WHERE ISDESCENDANTNODE('/test') AND NOT CONTAINS(foo, 'bar')",
+ of("/test/b", "/test/c"));
+ assertQuery(
+ "SELECT * FROM [nt:folder] WHERE ISDESCENDANTNODE('/test') AND CONTAINS(foo, 'bar cat')",
+ of("/test/d"));
+ assertQuery(
+ "SELECT * FROM [nt:folder] WHERE ISDESCENDANTNODE('/test') AND NOT CONTAINS(foo, 'bar cat')",
+ of("/test/c"));
+
+ setTraversalEnabled(true);
+ }
+}
Propchange: jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexAggregationCommonTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexDescendantSpellcheckCommonTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexDescendantSpellcheckCommonTest.java?rev=1880807&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexDescendantSpellcheckCommonTest.java (added)
+++ jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexDescendantSpellcheckCommonTest.java Wed Aug 12 13:46:45 2020
@@ -0,0 +1,214 @@
+/*
+ * 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;
+
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.api.JackrabbitSession;
+import org.apache.jackrabbit.commons.JcrUtils;
+import org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants;
+import org.apache.jackrabbit.oak.plugins.index.search.IndexFormatVersion;
+import org.apache.jackrabbit.oak.query.AbstractJcrTest;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import javax.jcr.Node;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryManager;
+import javax.jcr.query.QueryResult;
+import javax.jcr.query.RowIterator;
+import java.util.Set;
+
+import static com.google.common.collect.Sets.newHashSet;
+import static org.apache.jackrabbit.JcrConstants.NT_BASE;
+import static org.apache.jackrabbit.JcrConstants.NT_UNSTRUCTURED;
+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.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.EVALUATE_PATH_RESTRICTION;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.INDEX_RULES;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.PROPDEF_PROP_NODE_NAME;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.PROP_USE_IN_SPELLCHECK;
+import static org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants.NT_OAK_UNSTRUCTURED;
+import static org.junit.Assert.assertEquals;
+
+public abstract class IndexDescendantSpellcheckCommonTest extends AbstractJcrTest {
+ protected TestRepository repositoryOptionsUtil;
+ protected Node indexNode;
+ protected IndexOptions indexOptions;
+
+ protected JackrabbitSession session = null;
+ protected Node root = null;
+
+ @Override
+ protected void initialize() {
+ session = (JackrabbitSession) adminSession;
+ try {
+ root = adminSession.getRootNode();
+ createContent();
+ adminSession.save();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ }
+
+ protected void createContent() throws Exception {
+ /*
+ Make content with following structure:
+ * sugg-idx is a simple index to suggest node names on type "oak:Unstructured"
+ * test[1-6] nodes would be "oak:Unstructured".
+ * all other nodes, unless required are "nt:unstructured"
+ * Index on one sub-tree is on nt:base so that we can do sub-tree suggestion test with unambiguous indices
+ */
+
+ // /oak:index/spellcheck-idx, /test1
+ createSuggestIndex(root, "spellcheck-idx", NT_OAK_UNSTRUCTURED, PROPDEF_PROP_NODE_NAME);
+ root.addNode("test1", NT_OAK_UNSTRUCTURED);
+
+ /*
+ /content1
+ /tree1
+ /test2
+ /tree2
+ /test3
+ */
+ Node content1 = root.addNode("content1", NT_UNSTRUCTURED);
+ Node tree1 = content1.addNode("tree1", NT_UNSTRUCTURED);
+ tree1.addNode("test2", NT_OAK_UNSTRUCTURED);
+ Node tree2 = content1.addNode("tree2", NT_UNSTRUCTURED);
+ tree2.addNode("test3", NT_OAK_UNSTRUCTURED);
+
+ // /content2/oak:index/spellcheck-idx, /content2/test4
+ Node content2 = root.addNode("content2", NT_UNSTRUCTURED);
+ createSuggestIndex(content2, "spellcheck-idx", NT_OAK_UNSTRUCTURED, PROPDEF_PROP_NODE_NAME);
+ content2.addNode("test4", NT_OAK_UNSTRUCTURED);
+
+ // /content3/oak:index/spellcheck-idx, /content3/test5, /content3/sC/test6
+ Node content3 = root.addNode("content3", NT_UNSTRUCTURED);
+ createSuggestIndex(content3, "spellcheck-idx", NT_BASE, PROPDEF_PROP_NODE_NAME);
+ content3.addNode("test5", NT_OAK_UNSTRUCTURED);
+ Node subChild = content3.addNode("sC", NT_UNSTRUCTURED);
+ subChild.addNode("test6", NT_OAK_UNSTRUCTURED);
+ }
+
+ private void createSuggestIndex(Node rootNode, String name, String indexedNodeType, String indexedPropertyName)
+ throws Exception {
+ Node def = JcrUtils.getOrAddNode(rootNode, INDEX_DEFINITIONS_NAME)
+ .addNode(name, INDEX_DEFINITIONS_NODE_TYPE);
+ def.setProperty(TYPE_PROPERTY_NAME, indexOptions.getIndexType());
+ def.setProperty(REINDEX_PROPERTY_NAME, true);
+ def.setProperty("name", name);
+ def.setProperty(FulltextIndexConstants.COMPAT_MODE, IndexFormatVersion.V2.getVersion());
+ def.setProperty(EVALUATE_PATH_RESTRICTION, true);
+
+ Node propertyIdxDef = def.addNode(INDEX_RULES, JcrConstants.NT_UNSTRUCTURED)
+ .addNode(indexedNodeType, JcrConstants.NT_UNSTRUCTURED)
+ .addNode(FulltextIndexConstants.PROP_NODE, JcrConstants.NT_UNSTRUCTURED)
+ .addNode("indexedProperty", JcrConstants.NT_UNSTRUCTURED);
+ propertyIdxDef.setProperty("analyzed", true);
+ propertyIdxDef.setProperty(PROP_USE_IN_SPELLCHECK, true);
+ propertyIdxDef.setProperty("name", indexedPropertyName);
+ }
+
+ private String createSpellcheckQuery(String nodeTypeName, String suggestFor, String rootPath) {
+ return "SELECT [rep:spellcheck()] as spellcheck, [jcr:score] as score FROM [" + nodeTypeName + "]" +
+ " WHERE spellcheck('" + suggestFor + "')" +
+ (rootPath == null ? "" : " AND ISDESCENDANTNODE([" + rootPath + "])");
+
+ }
+
+ private Set<String> getSpellchecks(String query) throws Exception {
+ QueryManager queryManager = session.getWorkspace().getQueryManager();
+ QueryResult result = queryManager.createQuery(query, Query.JCR_SQL2).execute();
+ RowIterator rows = result.getRows();
+
+ Set<String> suggestions = newHashSet();
+ while (rows.hasNext()) {
+ suggestions.add(rows.nextRow().getValue("spellcheck").getString());
+ }
+
+ return suggestions;
+ }
+
+ private void validateSpellchecks(String query, Set<String> expected) throws Exception {
+ assertEventually(() -> {
+ Set<String> suggestions = null;
+ try {
+ suggestions = getSpellchecks(query);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ assertEquals("Incorrect suggestions", expected, suggestions);
+ });
+ }
+
+ @Ignore
+ //TODO ES Failing
+ @Test
+ public void noDescendantSuggestsAll() throws Exception {
+ validateSpellchecks(
+ createSpellcheckQuery(NT_OAK_UNSTRUCTURED, "taste", null),
+ newHashSet("test1", "test2", "test3", "test4", "test5", "test6"));
+ }
+
+ //OAK-3994
+ @Test
+ public void rootIndexWithDescendantConstraint() throws Exception {
+ validateSpellchecks(
+ createSpellcheckQuery(NT_OAK_UNSTRUCTURED, "taste", "/content1"),
+ newHashSet("test2", "test3"));
+ }
+
+ @Ignore
+ //TODO ES Failing: if path restriction is not enabled, all ruggestions should be returned
+ //OAK-3994
+ @Test
+ public void descendantSuggestionRequirePathRestrictionIndex() throws Exception {
+ Node rootIndexDef = root.getNode("oak:index/spellcheck-idx");
+ rootIndexDef.getProperty(EVALUATE_PATH_RESTRICTION).remove();
+ rootIndexDef.setProperty(REINDEX_PROPERTY_NAME, true);
+ session.save();
+
+ //Without path restriction indexing, descendant clause shouldn't be respected
+ validateSpellchecks(
+ createSpellcheckQuery(NT_OAK_UNSTRUCTURED, "taste", "/content1"),
+ newHashSet("test1", "test2", "test3", "test4", "test5", "test6"));
+ }
+
+ //@Ignore
+ //TODO ES Failing
+ //OAK-3994
+ @Test
+ public void unambiguousSubtreeIndexWithDescendantConstraint() throws Exception {
+ validateSpellchecks(
+ createSpellcheckQuery(NT_BASE, "taste", "/content3"),
+ newHashSet("test5", "test6"));
+ }
+
+ //OAK-3994
+ @Test
+ public void unambiguousSubtreeIndexWithSubDescendantConstraint() throws Exception {
+ validateSpellchecks(
+ createSpellcheckQuery(NT_BASE, "taste", "/content3/sC"),
+ newHashSet("test6"));
+ }
+
+ private static void assertEventually(Runnable r) {
+ TestUtils.assertEventually(r, 3000 * 3);
+ }
+}
Propchange: jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexDescendantSpellcheckCommonTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexDescendantSuggestionCommonTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexDescendantSuggestionCommonTest.java?rev=1880807&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexDescendantSuggestionCommonTest.java (added)
+++ jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexDescendantSuggestionCommonTest.java Wed Aug 12 13:46:45 2020
@@ -0,0 +1,238 @@
+/*
+ * 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;
+
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.api.JackrabbitSession;
+import org.apache.jackrabbit.commons.JcrUtils;
+import org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants;
+import org.apache.jackrabbit.oak.plugins.index.search.IndexFormatVersion;
+import org.apache.jackrabbit.oak.query.AbstractJcrTest;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import javax.jcr.Node;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryManager;
+import javax.jcr.query.QueryResult;
+import javax.jcr.query.RowIterator;
+import java.util.Set;
+
+import static com.google.common.collect.Sets.newHashSet;
+import static org.apache.jackrabbit.JcrConstants.NT_BASE;
+import static org.apache.jackrabbit.JcrConstants.NT_UNSTRUCTURED;
+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.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.EVALUATE_PATH_RESTRICTION;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.INDEX_RULES;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.PROPDEF_PROP_NODE_NAME;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.PROP_USE_IN_SUGGEST;
+import static org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants.NT_OAK_UNSTRUCTURED;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public abstract class IndexDescendantSuggestionCommonTest extends AbstractJcrTest {
+ protected TestRepository repositoryOptionsUtil;
+ protected Node indexNode;
+ protected IndexOptions indexOptions;
+
+ protected JackrabbitSession session = null;
+ protected Node root = null;
+
+ @Override
+ protected void initialize() {
+ session = (JackrabbitSession) adminSession;
+ try {
+ root = adminSession.getRootNode();
+ createContent();
+ adminSession.save();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ }
+
+ private void createContent() throws Exception {
+ /*
+ Make content with following structure:
+ * sugg-idx is a simple index to suggest node names on type "oak:Unstructured"
+ * test[1-6] nodes would be "oak:Unstructured".
+ * all other nodes, unless required are "nt:unstructured"
+ * Index on one sub-tree is on nt:base so that we can do sub-tree suggestion test with unambiguous indices
+ */
+
+ // /oak:index/sugg-idx, /test1
+ createSuggestIndex(root, "sugg-idx", NT_OAK_UNSTRUCTURED, PROPDEF_PROP_NODE_NAME);
+ root.addNode("test1", NT_OAK_UNSTRUCTURED);
+
+ /*
+ /content1
+ /tree1
+ /test2
+ /tree2
+ /test3
+ */
+ Node content1 = root.addNode("content1", NT_UNSTRUCTURED);
+ Node tree1 = content1.addNode("tree1", NT_UNSTRUCTURED);
+ tree1.addNode("test2", NT_OAK_UNSTRUCTURED);
+ Node tree2 = content1.addNode("tree2", NT_UNSTRUCTURED);
+ tree2.addNode("test3", NT_OAK_UNSTRUCTURED);
+
+ // /content2/oak:index/sugg-idx, /content2/test4
+ Node content2 = root.addNode("content2", NT_UNSTRUCTURED);
+ createSuggestIndex(content2, "sugg-idx", NT_OAK_UNSTRUCTURED, PROPDEF_PROP_NODE_NAME);
+ content2.addNode("test4", NT_OAK_UNSTRUCTURED);
+
+ // /content3/oak:index/sugg-idx, /content3/test5, /content3/sC/test6
+ Node content3 = root.addNode("content3", NT_UNSTRUCTURED);
+ createSuggestIndex(content3, "sugg-idx", NT_BASE, PROPDEF_PROP_NODE_NAME);
+ content3.addNode("test5", NT_OAK_UNSTRUCTURED);
+ Node subChild = content3.addNode("sC", NT_UNSTRUCTURED);
+ subChild.addNode("test6", NT_OAK_UNSTRUCTURED);
+ }
+
+ protected void createSuggestIndex(Node rootNode, String name, String indexedNodeType, String indexedPropertyName)
+ throws Exception {
+ Node def = JcrUtils.getOrAddNode(rootNode, INDEX_DEFINITIONS_NAME)
+ .addNode(name, INDEX_DEFINITIONS_NODE_TYPE);
+ def.setProperty(TYPE_PROPERTY_NAME, indexOptions.getIndexType());
+ def.setProperty(REINDEX_PROPERTY_NAME, true);
+ def.setProperty("name", name);
+ def.setProperty(FulltextIndexConstants.COMPAT_MODE, IndexFormatVersion.V2.getVersion());
+ def.setProperty(EVALUATE_PATH_RESTRICTION, true);
+
+ Node propertyIdxDef = def.addNode(INDEX_RULES, JcrConstants.NT_UNSTRUCTURED)
+ .addNode(indexedNodeType, JcrConstants.NT_UNSTRUCTURED)
+ .addNode(FulltextIndexConstants.PROP_NODE, JcrConstants.NT_UNSTRUCTURED)
+ .addNode("indexedProperty", JcrConstants.NT_UNSTRUCTURED);
+ propertyIdxDef.setProperty("analyzed", true);
+ propertyIdxDef.setProperty(PROP_USE_IN_SUGGEST, true);
+ propertyIdxDef.setProperty("name", indexedPropertyName);
+ }
+
+ private String createSuggestQuery(String nodeTypeName, String suggestFor, String rootPath) {
+ return "SELECT [rep:suggest()] as suggestion, [jcr:score] as score FROM [" + nodeTypeName + "]" +
+ " WHERE suggest('" + suggestFor + "')" +
+ (rootPath == null ? "" : " AND ISDESCENDANTNODE([" + rootPath + "])");
+
+ }
+
+ private Set<String> getSuggestions(String query) throws Exception {
+ QueryManager queryManager = session.getWorkspace().getQueryManager();
+ QueryResult result = queryManager.createQuery(query, Query.JCR_SQL2).execute();
+ RowIterator rows = result.getRows();
+
+ Set<String> suggestions = newHashSet();
+ while (rows.hasNext()) {
+ suggestions.add(rows.nextRow().getValue("suggestion").getString());
+ }
+
+ return suggestions;
+ }
+
+ private void validateSuggestions(String query, Set<String> expected) throws Exception {
+ assertEventually(() -> {
+ Set<String> suggestions = null;
+ try {
+ suggestions = getSuggestions(query);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ assertEquals("Incorrect suggestions", expected, suggestions);
+ });
+ }
+
+ //Don't break suggestions :)
+ @Test
+ public void noDescendantSuggestsAll() throws Exception {
+ validateSuggestions(
+ createSuggestQuery(NT_OAK_UNSTRUCTURED, "te", null),
+ newHashSet("test1", "test2", "test3", "test4", "test5", "test6"));
+ }
+
+ //OAK-3994
+ @Test
+ public void rootIndexWithDescendantConstraint() throws Exception {
+ validateSuggestions(
+ createSuggestQuery(NT_OAK_UNSTRUCTURED, "te", "/content1"),
+ newHashSet("test2", "test3"));
+ }
+
+ @Ignore
+ //TODO ES failing --> if path restrictions are not enabled can we still get results with descendant filter as if path restrictions were enabled?
+ //OAK-3994
+ @Test
+ public void descendantSuggestionRequirePathRestrictionIndex() throws Exception {
+ Node rootIndexDef = root.getNode("oak:index/sugg-idx");
+ rootIndexDef.getProperty(EVALUATE_PATH_RESTRICTION).remove();
+ rootIndexDef.setProperty(REINDEX_PROPERTY_NAME, true);
+ session.save();
+
+ //Without path restriction indexing, descendant clause shouldn't be respected
+ validateSuggestions(
+ createSuggestQuery(NT_OAK_UNSTRUCTURED, "te", "/content1"),
+ newHashSet("test1", "test2", "test3", "test4", "test5", "test6"));
+ }
+
+ @Ignore("OAK-3992")
+ @Test
+ public void ambiguousSubtreeIndexWithDescendantConstraint() throws Exception {
+ String query = createSuggestQuery(NT_OAK_UNSTRUCTURED, "te", "/content2");
+ String explainQuery = "EXPLAIN " + createSuggestQuery(NT_OAK_UNSTRUCTURED, "te", "/content2");
+
+ QueryManager queryManager = session.getWorkspace().getQueryManager();
+ QueryResult result = queryManager.createQuery(explainQuery, Query.JCR_SQL2).execute();
+ RowIterator rows = result.getRows();
+
+ String explanation = rows.nextRow().toString();
+ assertTrue("Subtree index should get picked", explanation.contains("lucene:sugg-idx(/content2/oak:index/sugg-idx)"));
+
+ validateSuggestions(query, newHashSet("test4"));
+ }
+
+ //OAK-3994
+ @Test
+ public void unambiguousSubtreeIndexWithDescendantConstraint() throws Exception {
+ validateSuggestions(
+ createSuggestQuery(NT_BASE, "te", "/content3"),
+ newHashSet("test5", "test6"));
+ }
+
+ //OAK-3994
+ @Test
+ public void unambiguousSubtreeIndexWithSubDescendantConstraint() throws Exception {
+ validateSuggestions(
+ createSuggestQuery(NT_BASE, "te", "/content3/sC"),
+ newHashSet("test6"));
+ }
+
+ @Ignore("OAK-3993")
+ @Test
+ public void unionOnTwoDescendants() throws Exception {
+ validateSuggestions(
+ createSuggestQuery(NT_OAK_UNSTRUCTURED, "te", "/content1") +
+ " UNION " +
+ createSuggestQuery(NT_BASE, "te", "/content3"),
+ newHashSet("test2", "test3", "test5"));
+ }
+
+ private static void assertEventually(Runnable r) {
+ TestUtils.assertEventually(r, 3000 * 3);
+ }
+}
Propchange: jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexDescendantSuggestionCommonTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexExclusionQueryCommonTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexExclusionQueryCommonTest.java?rev=1880807&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexExclusionQueryCommonTest.java (added)
+++ jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexExclusionQueryCommonTest.java Wed Aug 12 13:46:45 2020
@@ -0,0 +1,97 @@
+/*
+ * 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;
+
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.query.AbstractQueryTest;
+import org.junit.Test;
+
+import java.util.List;
+
+import static com.google.common.collect.ImmutableList.of;
+import static javax.jcr.PropertyType.TYPENAME_BINARY;
+import static javax.jcr.PropertyType.TYPENAME_STRING;
+import static org.apache.jackrabbit.JcrConstants.JCR_LASTMODIFIED;
+import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
+import static org.apache.jackrabbit.JcrConstants.NT_UNSTRUCTURED;
+import static org.apache.jackrabbit.oak.api.Type.DATE;
+import static org.apache.jackrabbit.oak.api.Type.STRINGS;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.EXCLUDE_PROPERTY_NAMES;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.INCLUDE_PROPERTY_TYPES;
+
+/**
+ * Tests the IndexProvider exclusion settings
+ */
+public abstract class IndexExclusionQueryCommonTest extends AbstractQueryTest {
+
+ private static final String NOT_IN = "notincluded";
+ protected IndexOptions indexOptions;
+
+ @Override
+ protected void createTestIndexNode() throws Exception {
+ Tree lucene = createTestIndexNode(root.getTree("/"), indexOptions.getIndexType());
+ lucene.setProperty(INCLUDE_PROPERTY_TYPES,
+ of(TYPENAME_BINARY, TYPENAME_STRING), STRINGS);
+ lucene.setProperty(EXCLUDE_PROPERTY_NAMES, of(NOT_IN), STRINGS);
+ TestUtil.useV2(lucene);
+ root.commit();
+ }
+
+ @Test
+ public void ignoreByType() throws Exception {
+ Tree content = root.getTree("/").addChild("content");
+ Tree one = content.addChild("one");
+ one.setProperty(JCR_PRIMARYTYPE, NT_UNSTRUCTURED);
+ one.setProperty(JCR_LASTMODIFIED, "2013-04-01T09:58:03.231Z", DATE);
+ one.setProperty("jcr:title", "abc");
+
+ Tree two = content.addChild("two");
+ two.setProperty(JCR_PRIMARYTYPE, NT_UNSTRUCTURED);
+ two.setProperty(JCR_LASTMODIFIED, "2014-04-01T09:58:03.231Z", DATE);
+ two.setProperty("jcr:title", "abc");
+
+ root.commit();
+
+ String query = "/jcr:root/content//*[jcr:contains(., 'abc' )"
+ + " and (@" + JCR_LASTMODIFIED
+ + " > xs:dateTime('2014-04-01T08:58:03.231Z')) ]";
+ assertQuery(query, "xpath", of("/content/two"));
+ }
+
+ @Test
+ public void ignoreByName() throws Exception {
+ final List<String> expected = of("/content/two");
+
+ Tree content = root.getTree("/").addChild("content");
+ Tree one = content.addChild("one");
+ one.setProperty(JCR_PRIMARYTYPE, NT_UNSTRUCTURED);
+ one.setProperty("jcr:title", "abc");
+ one.setProperty(NOT_IN, "azerty");
+
+ Tree two = content.addChild("two");
+ two.setProperty(JCR_PRIMARYTYPE, NT_UNSTRUCTURED);
+ two.setProperty("jcr:title", "abc");
+ two.setProperty(NOT_IN, "querty");
+
+ root.commit();
+
+ String query = "/jcr:root/content//*[jcr:contains(., 'abc' )"
+ + " and (@" + NOT_IN + " = 'querty') ]";
+ assertQuery(query, "xpath", expected);
+ }
+
+}
Propchange: jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexExclusionQueryCommonTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexQueryCommonTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexQueryCommonTest.java?rev=1880807&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexQueryCommonTest.java (added)
+++ jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexQueryCommonTest.java Wed Aug 12 13:46:45 2020
@@ -0,0 +1,605 @@
+/*
+ * 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;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants;
+import org.apache.jackrabbit.oak.query.AbstractQueryTest;
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import static com.google.common.collect.ImmutableList.of;
+import static java.util.Arrays.asList;
+import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
+import static org.apache.jackrabbit.JcrConstants.NT_UNSTRUCTURED;
+import static org.apache.jackrabbit.oak.api.Type.STRING;
+import static org.apache.jackrabbit.oak.api.Type.STRINGS;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.REINDEX_PROPERTY_NAME;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.PROP_VALUE_REGEX;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests the query engine using the default index implementation: the
+ * IndexProvider
+ */
+public abstract class IndexQueryCommonTest extends AbstractQueryTest {
+
+ private Tree indexDefn;
+ protected IndexOptions indexOptions;
+ protected TestRepository repositoryOptionsUtil;
+
+ @Override
+ protected void createTestIndexNode() throws Exception {
+ setTraversalEnabled(false);
+ Tree index = root.getTree("/");
+ indexDefn = createTestIndexNode(index, indexOptions.getIndexType());
+ TestUtil.useV2(indexDefn);
+ indexDefn.setProperty(FulltextIndexConstants.EVALUATE_PATH_RESTRICTION, true);
+
+ Tree props = TestUtil.newRulePropTree(indexDefn, "nt:base");
+ props.getParent().setProperty(FulltextIndexConstants.INDEX_NODE_NAME, true);
+ TestUtil.enablePropertyIndex(props, "c1/p", false);
+ TestUtil.enableForFullText(props, FulltextIndexConstants.REGEX_ALL_PROPS, true);
+ TestUtil.enablePropertyIndex(props, "a/name", false);
+ TestUtil.enablePropertyIndex(props, "b/name", false);
+ TestUtil.enableFunctionIndex(props, "length([name])");
+ TestUtil.enableFunctionIndex(props, "lower([name])");
+ TestUtil.enableFunctionIndex(props, "upper([name])");
+
+ root.commit();
+ }
+
+ @Ignore
+ //TODO ES failing
+ @Test
+ public void sql1() throws Exception {
+ test("sql1.txt");
+ }
+
+ @Ignore
+ //TODO ES Failing
+ @Test
+ public void sql2() throws Exception {
+ test("sql2.txt");
+ }
+
+ //TODO ES test failing
+ @Ignore
+ @Test
+ public void sql2FullText() throws Exception {
+ test("sql2-fulltext.txt");
+ }
+
+ @Ignore
+ @Test
+ public void testValueRegex() throws Exception {
+ Tree test = root.getTree("/").addChild("test");
+ Tree a = test.addChild("a");
+ Tree b = test.addChild("b");
+ a.setProperty("name", "hello");
+ b.setProperty("name", "hello pattern");
+ root.commit();
+
+ final String query = "select [jcr:path] from [nt:base] where isdescendantnode('/test') and contains(*, 'hello')";
+
+ assertEventually(() -> {
+ Iterator<String> result = executeQuery(query, "JCR-SQL2").iterator();
+ List<String> paths = new ArrayList<>();
+ result.forEachRemaining(paths::add);
+ assertEquals(2, paths.size());
+ assertEquals(paths.get(0), a.getPath());
+ assertEquals(paths.get(1), b.getPath());
+ });
+
+ indexDefn.setProperty(PROP_VALUE_REGEX, "pat*");
+ indexDefn.setProperty(REINDEX_PROPERTY_NAME, true);
+ root.commit();
+
+ assertEventually(() -> {
+ Iterator<String> result = executeQuery(query, "JCR-SQL2").iterator();
+ List<String> paths = new ArrayList<>();
+ ;
+ result.forEachRemaining(paths::add);
+ assertEquals(1, paths.size());
+ assertEquals(paths.get(0), b.getPath());
+ });
+
+ }
+
+ @Test
+ public void descendantTest() throws Exception {
+ Tree test = root.getTree("/").addChild("test");
+ test.addChild("a");
+ test.addChild("b");
+ root.commit();
+
+ assertEventually(() -> {
+ Iterator<String> result = executeQuery(
+ "select [jcr:path] from [nt:base] where isdescendantnode('/test')",
+ "JCR-SQL2").iterator();
+ assertTrue(result.hasNext());
+ assertEquals("/test/a", result.next());
+ assertEquals("/test/b", result.next());
+ assertFalse(result.hasNext());
+ });
+ }
+
+ //TODO ES failing
+ @Ignore
+ @Test
+ public void descendantTest2() throws Exception {
+ Tree test = root.getTree("/").addChild("test");
+ test.addChild("a").setProperty("name", asList("Hello", "World"), STRINGS);
+ test.addChild("b").setProperty("name", "Hello");
+ root.commit();
+
+ assertEventually(() -> {
+ Iterator<String> result = executeQuery(
+ "select [jcr:path] from [nt:base] where isdescendantnode('/test') and name='World'",
+ "JCR-SQL2").iterator();
+ assertTrue(result.hasNext());
+ assertEquals("/test/a", result.next());
+ assertFalse(result.hasNext());
+ });
+ }
+
+ //TODO ES Failing
+ @Ignore
+ @Test
+ public void ischildnodeTest() throws Exception {
+ Tree tree = root.getTree("/");
+ Tree parents = tree.addChild("parents");
+ parents.addChild("p0").setProperty("id", "0");
+ parents.addChild("p1").setProperty("id", "1");
+ parents.addChild("p2").setProperty("id", "2");
+ Tree children = tree.addChild("children");
+ children.addChild("c1").setProperty("p", "1");
+ children.addChild("c2").setProperty("p", "2");
+ children.addChild("c3").setProperty("p", "3");
+ children.addChild("c4").setProperty("p", "4");
+ root.commit();
+
+ assertEventually(() -> {
+ Iterator<String> result = executeQuery(
+ "select p.[jcr:path], p2.[jcr:path] from [nt:base] as p inner join [nt:base] as p2 on ischildnode(p2, p) where p.[jcr:path] = '/'",
+ "JCR-SQL2").iterator();
+ assertTrue(result.hasNext());
+ assertEquals("/, /children", result.next());
+ assertEquals("/, /jcr:system", result.next());
+ assertEquals("/, /oak:index", result.next());
+ assertEquals("/, /parents", result.next());
+ assertFalse(result.hasNext());
+ });
+ }
+
+ @Ignore
+ @Test
+ public void contains() throws Exception {
+ String h = "Hello" + System.currentTimeMillis();
+ String w = "World" + System.currentTimeMillis();
+
+ Tree test = root.getTree("/").addChild("test");
+ test.addChild("a").setProperty("name", asList(h, w), STRINGS);
+ test.addChild("b").setProperty("name", h);
+ root.commit();
+
+ // query 'hello'
+ final StringBuffer stmt = new StringBuffer();
+ stmt.append("/jcr:root//*[jcr:contains(., '").append(h);
+ stmt.append("')]");
+ assertEventually(() -> {
+ assertQuery(stmt.toString(), "xpath",
+ ImmutableList.of("/test/a", "/test/b"));
+ });
+
+ // query 'world'
+ final StringBuffer stmt1 = new StringBuffer();
+ stmt1.append("/jcr:root//*[jcr:contains(., '").append(w);
+ stmt1.append("')]");
+ assertEventually(() -> {
+ assertQuery(stmt1.toString(), "xpath", ImmutableList.of("/test/a"));
+ });
+
+ }
+
+ //TODO ES failing
+ @Ignore
+ @Test
+ public void containsNot() throws Exception {
+
+ // see also OAK-3371
+ // "if we have only NOT CLAUSES we have to add a match all docs (*.*) for the
+ // query to work"
+
+ executeQuery("/jcr:root//*[jcr:contains(@a,'-test*')]", "xpath", false);
+
+ String planPrefix = "[nt:base] as [a] /* lucene:test-index(/oak:index/test-index) ";
+
+ assertXPathPlan("/jcr:root//*[@a]",
+ planPrefix + "a:[* TO *]");
+
+ assertXPathPlan("/jcr:root//*[jcr:contains(., '*')]",
+ planPrefix + ":fulltext:* ft:(\"*\")");
+
+ assertXPathPlan("/jcr:root//*[jcr:contains(@a,'*')]",
+ planPrefix + "full:a:* ft:(a:\"*\")");
+
+ assertXPathPlan("/jcr:root//*[jcr:contains(@a,'hello -world')]",
+ planPrefix + "+full:a:hello -full:a:world ft:(a:\"hello\" -a:\"world\")");
+
+ assertXPathPlan("/jcr:root//*[jcr:contains(@a,'test*')]",
+ planPrefix + "full:a:test* ft:(a:\"test*\")");
+
+ assertXPathPlan("/jcr:root//*[jcr:contains(@a,'-test')]",
+ planPrefix + "-full:a:test *:* ft:(-a:\"test\")");
+
+ assertXPathPlan("/jcr:root//*[jcr:contains(@a,'-test*')]",
+ planPrefix + "-full:a:test* *:* ft:(-a:\"test*\")");
+
+ assertXPathPlan("/jcr:root//*[jcr:contains(., '-*')]",
+ planPrefix + "-:fulltext:* *:* ft:(-\"*\")");
+
+ assertXPathPlan("/jcr:root//*[jcr:contains(., 'apple - pear')]",
+ planPrefix + "+:fulltext:apple -:fulltext:pear ft:(\"apple\" \"-\" \"pear\")");
+
+ assertXPathPlan("/jcr:root/content//*[jcr:contains(., 'apple - pear')]",
+ planPrefix + "-:fulltext:pear +:fulltext:apple +:ancestors:/content ft:(\"apple\" \"-\" \"pear\")");
+
+ }
+
+ private void assertXPathPlan(String xpathQuery, String expectedPlan) {
+ List<String> result = executeQuery("explain " + xpathQuery, "xpath", false);
+ String plan = result.get(0);
+ int newline = plan.indexOf('\n');
+ if (newline >= 0) {
+ plan = plan.substring(0, newline);
+ }
+ Assert.assertEquals(expectedPlan, plan);
+ }
+
+ @Ignore("OAK-2424")
+ @Test
+ public void containsDash() throws Exception {
+ Tree test = root.getTree("/").addChild("test");
+ test.addChild("a").setProperty("name", "hello-wor");
+ test.addChild("b").setProperty("name", "hello-world");
+ test.addChild("c").setProperty("name", "hello");
+ root.commit();
+
+ assertQuery("/jcr:root//*[jcr:contains(., 'hello-wor*')]", "xpath",
+ ImmutableList.of("/test/a", "/test/b"));
+ assertQuery("/jcr:root//*[jcr:contains(., '*hello-wor*')]", "xpath",
+ ImmutableList.of("/test/a", "/test/b"));
+
+ }
+
+ @Ignore("OAK-2424")
+ @Test
+ public void multiPhraseQuery() throws Exception {
+ Tree test = root.getTree("/").addChild("test");
+ test.addChild("a").setProperty("dc:format", "type:application/pdf");
+ test.addChild("b").setProperty("dc:format", "progress");
+ root.commit();
+
+ assertQuery(
+ "/jcr:root//*[jcr:contains(@dc:format, 'pro*')]",
+ "xpath", ImmutableList.of("/test/b"));
+
+
+ assertQuery(
+ "/jcr:root//*[jcr:contains(@dc:format, 'type:appli*')]",
+ "xpath", ImmutableList.of("/test/a"));
+
+ }
+
+ @Ignore
+ @Test
+ public void containsPath() throws Exception {
+
+ Tree test = root.getTree("/").addChild("test");
+ test.addChild("a").setProperty("name", "/parent/child/node");
+ root.commit();
+
+ StringBuffer stmt = new StringBuffer();
+ stmt.append("//*[jcr:contains(., '/parent/child')]");
+ assertEventually(() -> {
+ assertQuery(stmt.toString(), "xpath", ImmutableList.of("/test/a"));
+ });
+
+ }
+
+ @Ignore
+ @Test
+ public void containsPathNum() throws Exception {
+
+ Tree test = root.getTree("/").addChild("test");
+ Tree a = test.addChild("a");
+ a.setProperty("name", "/segment1/segment2/segment3");
+ root.commit();
+
+ StringBuffer stmt = new StringBuffer();
+ stmt.append("//*[jcr:contains(., '/segment1/segment2')]");
+ assertEventually(() -> {
+ assertQuery(stmt.toString(), "xpath", ImmutableList.of("/test/a"));
+ });
+
+ }
+
+ @Test
+ public void containsPathStrict() throws Exception {
+ root.getTree("/").addChild("matchOnPath");
+ root.getTree("/").addChild("match_on_path");
+ root.commit();
+
+ StringBuffer stmt = new StringBuffer();
+ stmt.append("//*[jcr:contains(., 'match')]");
+ assertEventually(() -> {
+ assertQuery(stmt.toString(), "xpath",
+ ImmutableList.of("/match_on_path"));
+ });
+
+ }
+
+ @Test
+ public void containsPathStrictNum() throws Exception {
+ root.getTree("/").addChild("matchOnPath1234");
+ root.getTree("/").addChild("match_on_path1234");
+ root.commit();
+
+ StringBuffer stmt = new StringBuffer();
+ stmt.append("//*[jcr:contains(., 'match')]");
+ assertEventually(() -> {
+ assertQuery(stmt.toString(), "xpath",
+ ImmutableList.of("/match_on_path1234"));
+ });
+
+ }
+
+ /**
+ * OAK-1208 property existence constraints break queries
+ */
+ //TODO ES failing
+ @Ignore
+ @Test
+ public void testOAK1208() throws Exception {
+ Tree t = root.getTree("/").addChild("containsWithMultipleOr");
+ Tree one = t.addChild("one");
+ one.setProperty("p", "dam/smartcollection");
+ one.setProperty("t", "media");
+
+ Tree two = t.addChild("two");
+ two.setProperty("p", "dam/collection");
+ two.setProperty("t", "media");
+
+ Tree three = t.addChild("three");
+ three.setProperty("p", "dam/hits");
+ three.setProperty("t", "media");
+
+ root.commit();
+
+ StringBuffer stmt = new StringBuffer();
+ stmt.append("//*[jcr:contains(., 'media') and (@p = 'dam/smartcollection' or @p = 'dam/collection') ]");
+ assertEventually(() -> {
+ assertQuery(stmt.toString(), "xpath",
+ ImmutableList.of(one.getPath(), two.getPath()));
+ });
+ }
+
+ @Test
+ public void testNativeLuceneQuery() throws Exception {
+ String nativeQueryString = "select [jcr:path] from [nt:base] where native('lucene', 'title:foo -title:bar')";
+ Tree test = root.getTree("/").addChild("test");
+ test.addChild("a").setProperty("title", "foo");
+ test.addChild("b").setProperty("title", "bar");
+ root.commit();
+
+ assertEventually(() -> {
+ Iterator<String> result = executeQuery(nativeQueryString, "JCR-SQL2").iterator();
+ assertTrue(result.hasNext());
+ assertEquals("/test/a", result.next());
+ assertFalse(result.hasNext());
+ });
+ }
+
+ //TODO ES Failing
+ @Ignore
+ @Test
+ public void testRepSimilarAsNativeQuery() throws Exception {
+ String nativeQueryString = "select [jcr:path] from [nt:base] where " +
+ "native('lucene', 'mlt?stream.body=/test/a&mlt.fl=:path&mlt.mindf=0&mlt.mintf=0')";
+ Tree test = root.getTree("/").addChild("test");
+ test.addChild("a").setProperty("text", "Hello World");
+ test.addChild("b").setProperty("text", "He said Hello and then the world said Hello as well.");
+ test.addChild("c").setProperty("text", "He said Hi.");
+ root.commit();
+ assertEventually(() -> {
+ Iterator<String> result = executeQuery(nativeQueryString, "JCR-SQL2").iterator();
+ assertTrue(result.hasNext());
+ assertEquals("/test/a", result.next());
+ assertTrue(result.hasNext());
+ assertEquals("/test/b", result.next());
+ assertFalse(result.hasNext());
+ });
+ }
+
+ //TODO ES Failing
+ @Ignore
+ @Test
+ public void testRepSimilarQuery() throws Exception {
+ String query = "select [jcr:path] from [nt:base] where similar(., '/test/a')";
+ Tree test = root.getTree("/").addChild("test");
+ test.addChild("a").setProperty("text", "Hello World Hello World");
+ test.addChild("b").setProperty("text", "Hello World");
+ test.addChild("c").setProperty("text", "World");
+ test.addChild("d").setProperty("text", "Hello");
+ test.addChild("e").setProperty("text", "World");
+ test.addChild("f").setProperty("text", "Hello");
+ test.addChild("g").setProperty("text", "World");
+ test.addChild("h").setProperty("text", "Hello");
+ root.commit();
+ assertEventually(() -> {
+ Iterator<String> result = executeQuery(query, "JCR-SQL2").iterator();
+ assertTrue(result.hasNext());
+ assertEquals("/test/a", result.next());
+ assertTrue(result.hasNext());
+ assertEquals("/test/b", result.next());
+ assertTrue(result.hasNext());
+ });
+ }
+
+ //TODO ES failing
+ @Ignore
+ @Test
+ public void testRepSimilarXPathQuery() throws Exception {
+ String query = "//element(*, nt:base)[rep:similar(., '/test/a')]";
+ Tree test = root.getTree("/").addChild("test");
+ test.addChild("a").setProperty("text", "Hello World Hello World");
+ test.addChild("b").setProperty("text", "Hello World");
+ test.addChild("c").setProperty("text", "World");
+ test.addChild("d").setProperty("text", "Hello");
+ test.addChild("e").setProperty("text", "World");
+ test.addChild("f").setProperty("text", "Hello");
+ test.addChild("g").setProperty("text", "World");
+ test.addChild("h").setProperty("text", "Hello");
+ root.commit();
+ assertEventually(() -> {
+ Iterator<String> result = executeQuery(query, "xpath").iterator();
+ assertTrue(result.hasNext());
+ assertEquals("/test/a", result.next());
+ assertTrue(result.hasNext());
+ assertEquals("/test/b", result.next());
+ });
+ }
+
+ @Ignore
+ @Test
+ public void testTokenizeCN() throws Exception {
+ Tree t = root.getTree("/").addChild("containsCN");
+ Tree one = t.addChild("one");
+ one.setProperty("t", "ç¾å¥³è¡¬è¡«");
+ root.commit();
+ assertEventually(() -> {
+ assertQuery("//*[jcr:contains(., 'ç¾å¥³')]", "xpath",
+ ImmutableList.of(one.getPath()));
+ });
+ }
+
+ //TODO ES Failing
+ @Ignore
+ @Test
+ public void testMultiValuedPropUpdate() throws Exception {
+ Tree test = root.getTree("/").addChild("test");
+ String child = "child";
+ String mulValuedProp = "prop";
+ test.addChild(child).setProperty(mulValuedProp, of("foo", "bar"), Type.STRINGS);
+ root.commit();
+ assertEventually(() -> {
+ assertQuery(
+ "/jcr:root//*[jcr:contains(@" + mulValuedProp + ", 'foo')]",
+ "xpath", of("/test/" + child));
+ });
+ test.getChild(child).setProperty(mulValuedProp, new ArrayList<String>(), Type.STRINGS);
+ root.commit();
+ assertEventually(() -> {
+ assertQuery("/jcr:root//*[jcr:contains(@" + mulValuedProp + ", 'foo')]", "xpath", new ArrayList<String>());
+ });
+ test.getChild(child).setProperty(mulValuedProp, of("bar"), Type.STRINGS);
+ root.commit();
+ assertEventually(() -> {
+ assertQuery(
+ "/jcr:root//*[jcr:contains(@" + mulValuedProp + ", 'foo')]",
+ "xpath", new ArrayList<String>());
+ });
+ test.getChild(child).removeProperty(mulValuedProp);
+ root.commit();
+ assertEventually(() -> {
+ assertQuery(
+ "/jcr:root//*[jcr:contains(@" + mulValuedProp + ", 'foo')]",
+ "xpath", new ArrayList<String>());
+ });
+ }
+
+ @SuppressWarnings("unused")
+ private static void walktree(final Tree t) {
+ System.out.println("+ " + t.getPath());
+ for (PropertyState p : t.getProperties()) {
+ System.out.println(" -" + p.getName() + "=" + p.getValue(STRING));
+ }
+ for (Tree t1 : t.getChildren()) {
+ walktree(t1);
+ }
+ }
+
+ private static Tree child(Tree t, String n, String type) {
+ Tree t1 = t.addChild(n);
+ t1.setProperty(JCR_PRIMARYTYPE, type, Type.NAME);
+ return t1;
+ }
+
+ //TODO ES TEST Failing
+ @Ignore
+ @Test
+ public void oak3371() throws Exception {
+ setTraversalEnabled(false);
+ Tree t, t1;
+
+ t = root.getTree("/");
+ t = child(t, "test", NT_UNSTRUCTURED);
+ t1 = child(t, "a", NT_UNSTRUCTURED);
+ t1.setProperty("foo", "bar");
+ t1 = child(t, "b", NT_UNSTRUCTURED);
+ t1.setProperty("foo", "cat");
+ t1 = child(t, "c", NT_UNSTRUCTURED);
+ t1 = child(t, "d", NT_UNSTRUCTURED);
+ t1.setProperty("foo", "bar cat");
+
+ root.commit();
+
+ assertEventually(() -> {
+ assertQuery(
+ "SELECT * FROM [nt:unstructured] WHERE ISDESCENDANTNODE('/test') AND CONTAINS(foo, 'bar')",
+ of("/test/a", "/test/d"));
+
+ assertQuery(
+ "SELECT * FROM [nt:unstructured] WHERE ISDESCENDANTNODE('/test') AND NOT CONTAINS(foo, 'bar')",
+ of("/test/b", "/test/c"));
+
+ assertQuery(
+ "SELECT * FROM [nt:unstructured] WHERE ISDESCENDANTNODE('/test') AND CONTAINS(foo, 'bar cat')",
+ of("/test/d"));
+
+ assertQuery(
+ "SELECT * FROM [nt:unstructured] WHERE ISDESCENDANTNODE('/test') AND NOT CONTAINS(foo, 'bar cat')",
+ of("/test/c"));
+ });
+ setTraversalEnabled(true);
+ }
+
+ private static void assertEventually(Runnable r) {
+ TestUtils.assertEventually(r, 3000 * 3);
+ }
+}
Propchange: jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/IndexQueryCommonTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/SecureFacetCommonTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/SecureFacetCommonTest.java?rev=1880807&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/SecureFacetCommonTest.java (added)
+++ jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/SecureFacetCommonTest.java Wed Aug 12 13:46:45 2020
@@ -0,0 +1,338 @@
+/*
+ * 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;
+
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.PerfLogger;
+import org.apache.jackrabbit.oak.plugins.index.search.util.IndexDefinitionBuilder;
+import org.apache.jackrabbit.oak.query.AbstractJcrTest;
+import org.apache.jackrabbit.oak.query.facet.FacetResult;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryResult;
+import javax.jcr.security.Privilege;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Random;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+import static org.apache.jackrabbit.commons.JcrUtils.getOrCreateByPath;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.FACETS;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.PROP_REFRESH_DEFN;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.PROP_SECURE_FACETS;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.PROP_SECURE_FACETS_VALUE_INSECURE;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.PROP_SECURE_FACETS_VALUE_STATISTICAL;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.PROP_STATISTICAL_FACET_SAMPLE_SIZE;
+import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.STATISTICAL_FACET_SAMPLE_SIZE_DEFAULT;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+public abstract class SecureFacetCommonTest extends AbstractJcrTest {
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractJcrTest.class);
+ private static final PerfLogger LOG_PERF = new PerfLogger(LOG);
+ protected TestRepository repositoryOptionsUtil;
+ protected Node indexNode;
+ protected IndexOptions indexOptions;
+ private static final String FACET_PROP = "facets";
+
+ private static final int NUM_LEAF_NODES = STATISTICAL_FACET_SAMPLE_SIZE_DEFAULT;
+ private static final int NUM_LABELS = 4;
+ private static final int NUM_LEAF_NODES_FOR_LARGE_DATASET = NUM_LEAF_NODES;
+ private static final int NUM_LEAF_NODES_FOR_SMALL_DATASET = NUM_LEAF_NODES / (2 * NUM_LABELS);
+ private final Map<String, Integer> actualLabelCount = new HashMap<>();
+ private final Map<String, Integer> actualAclLabelCount = new HashMap<>();
+ private final Map<String, Integer> actualAclPar1LabelCount = new HashMap<>();
+
+
+ @Before
+ public void createIndex() throws RepositoryException {
+ String indexName = UUID.randomUUID().toString();
+ IndexDefinitionBuilder builder = indexOptions.createIndex(indexOptions.createIndexDefinitionBuilder(), false);
+ builder.noAsync();
+ IndexDefinitionBuilder.IndexRule indexRule = builder.indexRule(JcrConstants.NT_BASE);
+ indexRule.property("cons").propertyIndex();
+ indexRule.property("foo").propertyIndex().getBuilderTree().setProperty(FACET_PROP, true, Type.BOOLEAN);
+ indexRule.property("bar").propertyIndex().getBuilderTree().setProperty(FACET_PROP, true, Type.BOOLEAN);
+
+ indexOptions.setIndex(adminSession, indexName, builder);
+ indexNode = indexOptions.getIndexNode(adminSession, indexName);
+ }
+
+ private void createDataset(int numberOfLeafNodes) throws RepositoryException {
+ Random rGen = new Random(42);
+ Random rGen1 = new Random(42);
+ int[] foolabelCount = new int[NUM_LABELS];
+ int[] fooaclLabelCount = new int[NUM_LABELS];
+ int[] fooaclPar1LabelCount = new int[NUM_LABELS];
+
+ int[] barlabelCount = new int[NUM_LABELS];
+ int[] baraclLabelCount = new int[NUM_LABELS];
+ int[] baraclPar1LabelCount = new int[NUM_LABELS];
+
+ Node par = allow(getOrCreateByPath("/parent", "oak:Unstructured", adminSession));
+
+ for (int i = 0; i < NUM_LABELS; i++) {
+ Node subPar = par.addNode("par" + i);
+ for (int j = 0; j < numberOfLeafNodes; j++) {
+ Node child = subPar.addNode("c" + j);
+ child.setProperty("cons", "val");
+ // Add a random label out of "l0", "l1", "l2", "l3"
+ int foolabelNum = rGen.nextInt(NUM_LABELS);
+ int barlabelNum = rGen1.nextInt(NUM_LABELS);
+ child.setProperty("foo", "l" + foolabelNum);
+ child.setProperty("bar", "m" + barlabelNum);
+
+ foolabelCount[foolabelNum]++;
+ barlabelCount[barlabelNum]++;
+ if (i != 0) {
+ fooaclLabelCount[foolabelNum]++;
+ baraclLabelCount[barlabelNum]++;
+ }
+ if (i == 1) {
+ fooaclPar1LabelCount[foolabelNum]++;
+ baraclPar1LabelCount[barlabelNum]++;
+ }
+ }
+
+ // deny access for one sub-parent
+ if (i == 0) {
+ deny(subPar);
+ }
+ }
+ adminSession.save();
+ for (int i = 0; i < foolabelCount.length; i++) {
+ actualLabelCount.put("l" + i, foolabelCount[i]);
+ actualLabelCount.put("m" + i, barlabelCount[i]);
+ actualAclLabelCount.put("l" + i, fooaclLabelCount[i]);
+ actualAclLabelCount.put("m" + i, baraclLabelCount[i]);
+ actualAclPar1LabelCount.put("l" + i, fooaclPar1LabelCount[i]);
+ actualAclPar1LabelCount.put("m" + i, baraclPar1LabelCount[i]);
+ }
+ assertNotEquals("Acl-ed and actual counts mustn't be same", actualLabelCount, actualAclLabelCount);
+ }
+
+ @Test
+ public void secureFacets() throws Exception {
+ createDataset(NUM_LEAF_NODES_FOR_LARGE_DATASET);
+ assertEventually(() -> assertEquals(actualAclLabelCount, getFacets()));
+ }
+
+ @Test
+ public void secureFacets_withOneLabelInaccessible() throws Exception {
+ createDataset(NUM_LEAF_NODES_FOR_LARGE_DATASET);
+ Node inaccessibleChild = deny(adminSession.getNode("/parent").addNode("par4")).addNode("c0");
+ inaccessibleChild.setProperty("cons", "val");
+ inaccessibleChild.setProperty("foo", "l4");
+ adminSession.save();
+ assertEventually(() -> assertEquals(actualAclLabelCount, getFacets()));
+ }
+
+ @Test
+ public void insecureFacets() throws Exception {
+ Node facetConfig = getOrCreateByPath(indexNode.getPath() + "/" + FACETS, "nt:unstructured", adminSession);
+ facetConfig.setProperty(PROP_SECURE_FACETS, PROP_SECURE_FACETS_VALUE_INSECURE);
+ adminSession.save();
+
+ createDataset(NUM_LEAF_NODES_FOR_LARGE_DATASET);
+ assertEventually(() -> assertEquals(actualLabelCount, getFacets()));
+ }
+
+ @Test
+ public void statisticalFacets() throws Exception {
+ Node facetConfig = getOrCreateByPath(indexNode.getPath() + "/" + FACETS, "nt:unstructured", adminSession);
+ facetConfig.setProperty(PROP_SECURE_FACETS, PROP_SECURE_FACETS_VALUE_STATISTICAL);
+ facetConfig.setProperty(PROP_STATISTICAL_FACET_SAMPLE_SIZE, 3000);
+ adminSession.save();
+
+ createDataset(NUM_LEAF_NODES_FOR_LARGE_DATASET);
+
+ assertEventually(() -> assertEquals("Unexpected number of facets", actualAclLabelCount.size(), getFacets().size()));
+
+ for (Map.Entry<String, Integer> facet : actualAclLabelCount.entrySet()) {
+ String facetLabel = facet.getKey();
+ assertEventually(() -> {
+ int facetCount = getFacets().get(facetLabel);
+ float ratio = ((float) facetCount) / facet.getValue();
+ assertTrue("Facet count for label: " + facetLabel + " is outside of 10% margin of error. " +
+ "Expected: " + facet.getValue() + "; Got: " + facetCount + "; Ratio: " + ratio,
+ Math.abs(ratio - 1) < 0.1);
+ });
+ }
+ }
+
+ @Test
+ public void statisticalFacetsWithHitCountLessThanSampleSize() throws Exception {
+ Node facetConfig = getOrCreateByPath(indexNode.getPath() + "/" + FACETS, "nt:unstructured", adminSession);
+ facetConfig.setProperty(PROP_SECURE_FACETS, PROP_SECURE_FACETS_VALUE_STATISTICAL);
+ indexNode.setProperty(PROP_REFRESH_DEFN, true);
+ adminSession.save();
+
+ createDataset(NUM_LEAF_NODES_FOR_SMALL_DATASET);
+
+ assertEventually(() -> assertEquals("Unexpected number of facets", actualAclLabelCount.size(), getFacets().size()));
+
+ // Since the hit count is less than sample size -> flow should have switched to secure facet count instead of statistical
+ // and thus the count should be exactly equal
+ assertEventually(() -> assertEquals(actualAclLabelCount, getFacets()));
+ }
+
+ //TODO Test is failing with lucene index.
+ @Ignore
+ @Test
+ public void statisticalFacets_withHitCountSameAsSampleSize() throws Exception {
+ Node facetConfig = getOrCreateByPath(indexNode.getPath() + "/" + FACETS, "nt:unstructured", adminSession);
+ facetConfig.setProperty(PROP_SECURE_FACETS, PROP_SECURE_FACETS_VALUE_STATISTICAL);
+ indexNode.setProperty(PROP_REFRESH_DEFN, true);
+ adminSession.save();
+
+ createDataset(NUM_LEAF_NODES_FOR_LARGE_DATASET);
+
+ assertEventually(() -> {
+ Map<String, Integer> facets = getFacets("/parent/par1");
+ assertEquals("Unexpected number of facets", actualAclPar1LabelCount.size(), facets.size());
+
+ for (Map.Entry<String, Integer> facet : actualAclPar1LabelCount.entrySet()) {
+ String facetLabel = facet.getKey();
+ int facetCount = facets.get(facetLabel);
+ float ratio = ((float) facetCount) / facet.getValue();
+ assertTrue("Facet count for label: " + facetLabel + " is outside of 10% margin of error. " +
+ "Expected: " + facet.getValue() + "; Got: " + facetCount + "; Ratio: " + ratio,
+ Math.abs(ratio - 1) < 0.1);
+ }
+ });
+ }
+
+ @Test
+ public void statisticalFacets_withOneLabelInaccessible() throws Exception {
+ Node facetConfig = getOrCreateByPath(indexNode.getPath() + "/" + FACETS, "nt:unstructured", adminSession);
+ facetConfig.setProperty(PROP_SECURE_FACETS, PROP_SECURE_FACETS_VALUE_STATISTICAL);
+ indexNode.setProperty(PROP_REFRESH_DEFN, true);
+ adminSession.save();
+
+ createDataset(NUM_LEAF_NODES_FOR_LARGE_DATASET);
+ Node inaccessibleChild = deny(adminSession.getNode("/parent").addNode("par4")).addNode("c0");
+ inaccessibleChild.setProperty("cons", "val");
+ inaccessibleChild.setProperty("foo", "l4");
+ adminSession.save();
+ assertEventually(() -> {
+ Map<String, Integer> facets = getFacets();
+ assertEquals("Unexpected number of facets", actualAclLabelCount.size(), facets.size());
+ });
+
+ for (Map.Entry<String, Integer> facet : actualAclLabelCount.entrySet()) {
+
+ assertEventually(() -> {
+ String facetLabel = facet.getKey();
+ int facetCount = getFacets().get(facetLabel);
+ float ratio = ((float) facetCount) / facet.getValue();
+ assertTrue("Facet count for label: " + facetLabel + " is outside of 10% margin of error. " +
+ "Expected: " + facet.getValue() + "; Got: " + facetCount + "; Ratio: " + ratio,
+ Math.abs(ratio - 1) < 0.1);
+ });
+ }
+ }
+
+ @Test
+ public void secureFacets_withAdminSession() throws Exception {
+ Node facetConfig = getOrCreateByPath(indexNode.getPath() + "/" + FACETS, "nt:unstructured", adminSession);
+ facetConfig.setProperty(PROP_SECURE_FACETS, PROP_SECURE_FACETS_VALUE_INSECURE);
+ indexNode.setProperty(PROP_REFRESH_DEFN, true);
+ adminSession.save();
+ createDataset(NUM_LEAF_NODES_FOR_LARGE_DATASET);
+ qm = adminSession.getWorkspace().getQueryManager();
+ assertEventually(() -> assertEquals(actualLabelCount, getFacets()));
+ }
+
+ @Test
+ public void statisticalFacets_withAdminSession() throws Exception {
+ Node facetConfig = getOrCreateByPath(indexNode.getPath() + "/" + FACETS, "nt:unstructured", adminSession);
+ facetConfig.setProperty(PROP_SECURE_FACETS, PROP_SECURE_FACETS_VALUE_STATISTICAL);
+ indexNode.setProperty(PROP_REFRESH_DEFN, true);
+ adminSession.save();
+ createDataset(NUM_LEAF_NODES_FOR_LARGE_DATASET);
+ qm = adminSession.getWorkspace().getQueryManager();
+ assertEventually(() -> {
+ Map<String, Integer> facets = getFacets();
+ assertEquals("Unexpected number of facets", actualLabelCount.size(), facets.size());
+ });
+
+ for (Map.Entry<String, Integer> facet : actualLabelCount.entrySet()) {
+ assertEventually(() -> {
+ String facetLabel = facet.getKey();
+ int facetCount = getFacets().get(facetLabel);
+ float ratio = ((float) facetCount) / facet.getValue();
+ assertTrue("Facet count for label: " + facetLabel + " is outside of 5% margin of error. " +
+ "Expected: " + facet.getValue() + "; Got: " + facetCount + "; Ratio: " + ratio,
+ Math.abs(ratio - 1) < 0.05);
+ });
+ }
+ }
+
+ private Map<String, Integer> getFacets() {
+ return getFacets(null);
+ }
+
+ private Node deny(Node node) throws RepositoryException {
+ AccessControlUtils.deny(node, "anonymous", Privilege.JCR_ALL);
+ return node;
+ }
+
+ private Node allow(Node node) throws RepositoryException {
+ AccessControlUtils.allow(node, "anonymous", Privilege.JCR_READ);
+ return node;
+ }
+
+ private Map<String, Integer> getFacets(String path) {
+ String pathCons = "";
+ if (path != null) {
+ pathCons = " AND ISDESCENDANTNODE('" + path + "')";
+ }
+ String query = "SELECT [rep:facet(foo)], [rep:facet(bar)] FROM [nt:base] WHERE [cons] = 'val'" + pathCons;
+ Query q;
+ QueryResult queryResult;
+ try {
+ q = qm.createQuery(query, Query.JCR_SQL2);
+ queryResult = q.execute();
+ } catch (RepositoryException e) {
+ throw new RuntimeException(e);
+ }
+ long start = LOG_PERF.start("Getting the Facet Results...");
+ FacetResult facetResult = new FacetResult(queryResult);
+ LOG_PERF.end(start, -1, "Facet Results fetched");
+
+ return facetResult.getDimensions()
+ .stream()
+ .flatMap(dim -> Objects.requireNonNull(facetResult.getFacets(dim)).stream())
+ .collect(Collectors.toMap(FacetResult.Facet::getLabel, FacetResult.Facet::getCount));
+ }
+
+ protected void assertEventually(Runnable r) {
+ TestUtils.assertEventually(r, ((repositoryOptionsUtil.isAsync() ? repositoryOptionsUtil.defaultAsyncIndexingTimeInSeconds : 0) + 3000) * 5);
+ }
+}
Propchange: jackrabbit/oak/trunk/oak-search/src/test/java/org/apache/jackrabbit/oak/plugins/index/SecureFacetCommonTest.java
------------------------------------------------------------------------------
svn:eol-style = native