Author: thomasm
Date: Thu Feb 27 10:44:08 2014
New Revision: 1572503

URL: http://svn.apache.org/r1572503
Log:
OAK-1325 Support native pass-through queries (e.g. Lucene)

Added:
    jackrabbit/oak/trunk/oak-solr-core/src/test/resources/org/
    jackrabbit/oak/trunk/oak-solr-core/src/test/resources/org/apache/
    jackrabbit/oak/trunk/oak-solr-core/src/test/resources/org/apache/jackrabbit/
    
jackrabbit/oak/trunk/oak-solr-core/src/test/resources/org/apache/jackrabbit/oak/
    
jackrabbit/oak/trunk/oak-solr-core/src/test/resources/org/apache/jackrabbit/oak/query/
    
jackrabbit/oak/trunk/oak-solr-core/src/test/resources/org/apache/jackrabbit/oak/query/native_solr.txt
Modified:
    
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrQueryIndex.java
    
jackrabbit/oak/trunk/oak-solr-core/src/main/resources/solr/oak/conf/schema.xml
    
jackrabbit/oak/trunk/oak-solr-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrIndexQueryTest.java
    
jackrabbit/oak/trunk/oak-solr-core/src/test/resources/solr/oak/conf/schema.xml

Modified: 
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrQueryIndex.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrQueryIndex.java?rev=1572503&r1=1572502&r2=1572503&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrQueryIndex.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-solr-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrQueryIndex.java
 Thu Feb 27 10:44:08 2014
@@ -18,13 +18,17 @@ package org.apache.jackrabbit.oak.plugin
 
 import java.util.Collection;
 
+import javax.annotation.CheckForNull;
+
 import org.apache.jackrabbit.oak.api.PropertyValue;
+import org.apache.jackrabbit.oak.plugins.index.aggregate.NodeAggregator;
 import 
org.apache.jackrabbit.oak.plugins.index.solr.configuration.OakSolrConfiguration;
 import org.apache.jackrabbit.oak.spi.query.Cursor;
 import org.apache.jackrabbit.oak.spi.query.Filter;
 import org.apache.jackrabbit.oak.spi.query.IndexRow;
 import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 import org.apache.jackrabbit.oak.spi.query.QueryIndex;
+import org.apache.jackrabbit.oak.spi.query.QueryIndex.FulltextQueryIndex;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.apache.solr.client.solrj.SolrQuery;
 import org.apache.solr.client.solrj.SolrServer;
@@ -37,19 +41,24 @@ import org.slf4j.LoggerFactory;
 /**
  * A Solr based {@link QueryIndex}
  */
-public class SolrQueryIndex implements QueryIndex {
+public class SolrQueryIndex implements FulltextQueryIndex {
 
+    public static final String NATIVE_SOLR_QUERY = "native*solr";
     private final Logger log = LoggerFactory.getLogger(SolrQueryIndex.class);
     public static final String TYPE = "solr";
 
     private final String name;
     private final SolrServer solrServer;
     private final OakSolrConfiguration configuration;
+    
+    private final NodeAggregator aggregator;
 
     public SolrQueryIndex(String name, SolrServer solrServer, 
OakSolrConfiguration configuration) {
         this.name = name;
         this.solrServer = solrServer;
         this.configuration = configuration;
+        // TODO this index should support aggregation in the same way as the 
Lucene index
+        this.aggregator = null;
     }
 
     @Override
@@ -63,6 +72,21 @@ public class SolrQueryIndex implements Q
         return (filter.getPropertyRestrictions() != null ? 
filter.getPropertyRestrictions().size() * 0.1 : 0)
                 + (filter.getFulltextConditions() != null ? 
filter.getFulltextConditions().size() * 0.01 : 0)
                 + (filter.getPathRestriction() != null ? 0.2 : 0);
+//        
+//        FullTextExpression ft = filter.getFullTextConstraint();
+//        if (ft == null) {
+//            // TODO solr should only be triggered for full-text conditions
+//            // return Double.POSITIVE_INFINITY;
+//        }
+//        int cost = 10;
+//        Collection<PropertyRestriction> restrictions = 
filter.getPropertyRestrictions();
+//        if (restrictions != null) {
+//            cost /= 2;
+//        }
+//        if (filter.getPathRestriction() != null) {
+//            cost /= 2;
+//        }
+//        return cost;
     }
 
     @Override
@@ -95,40 +119,45 @@ public class SolrQueryIndex implements Q
         Collection<Filter.PropertyRestriction> propertyRestrictions = 
filter.getPropertyRestrictions();
         if (propertyRestrictions != null && !propertyRestrictions.isEmpty()) {
             for (Filter.PropertyRestriction pr : propertyRestrictions) {
-                if (pr.propertyName.contains("/")) {
-                    // cannot handle child-level property restrictions
-                    continue;
-                }
-                String first = null;
-                if (pr.first != null) {
-                    first = 
partialEscape(String.valueOf(pr.first.getValue(pr.first.getType()))).toString();
-                }
-                String last = null;
-                if (pr.last != null) {
-                    last = 
partialEscape(String.valueOf(pr.last.getValue(pr.last.getType()))).toString();
-                }
-
-                String prField = 
configuration.getFieldForPropertyRestriction(pr);
-                CharSequence fieldName = partialEscape(prField != null ?
-                        prField : pr.propertyName);
-                if ("jcr\\:path".equals(fieldName.toString())) {
-                    queryBuilder.append(configuration.getPathField());
-                    queryBuilder.append(':');
-                    queryBuilder.append(first);
+                // native query support
+                if (NATIVE_SOLR_QUERY.equals(pr.propertyName)) {
+                    
queryBuilder.append(String.valueOf(pr.first.getValue(pr.first.getType())));
                 } else {
-                    queryBuilder.append(fieldName).append(':');
-                    if (pr.first != null && pr.last != null && 
pr.first.equals(pr.last)) {
+                    if (pr.propertyName.contains("/")) {
+                        // cannot handle child-level property restrictions
+                        continue;
+                    }
+                    String first = null;
+                    if (pr.first != null) {
+                        first = 
partialEscape(String.valueOf(pr.first.getValue(pr.first.getType()))).toString();
+                    }
+                    String last = null;
+                    if (pr.last != null) {
+                        last = 
partialEscape(String.valueOf(pr.last.getValue(pr.last.getType()))).toString();
+                    }
+
+                    String prField = 
configuration.getFieldForPropertyRestriction(pr);
+                    CharSequence fieldName = partialEscape(prField != null ?
+                            prField : pr.propertyName);
+                    if ("jcr\\:path".equals(fieldName.toString())) {
+                        queryBuilder.append(configuration.getPathField());
+                        queryBuilder.append(':');
                         queryBuilder.append(first);
-                    } else if (pr.first == null && pr.last == null) {
-                        queryBuilder.append('*');
-                    } else if ((pr.first != null && pr.last == null) || 
(pr.last != null && pr.first == null) || (!pr.first.equals(pr.last))) {
-                        // TODO : need to check if this works for all field 
types (most likely not!)
-                        queryBuilder.append(createRangeQuery(first, last, 
pr.firstIncluding, pr.lastIncluding));
-                    } else if (pr.isLike) {
-                        // TODO : the current parameter substitution is not 
expected to work well
-                        
queryBuilder.append(partialEscape(String.valueOf(pr.first.getValue(pr.first.getType())).replace('%',
 '*').replace('_', '?')));
                     } else {
-                        throw new RuntimeException("[unexpected!] not handled 
case");
+                        queryBuilder.append(fieldName).append(':');
+                        if (pr.first != null && pr.last != null && 
pr.first.equals(pr.last)) {
+                            queryBuilder.append(first);
+                        } else if (pr.first == null && pr.last == null) {
+                            queryBuilder.append('*');
+                        } else if ((pr.first != null && pr.last == null) || 
(pr.last != null && pr.first == null) || (!pr.first.equals(pr.last))) {
+                            // TODO : need to check if this works for all 
field types (most likely not!)
+                            queryBuilder.append(createRangeQuery(first, last, 
pr.firstIncluding, pr.lastIncluding));
+                        } else if (pr.isLike) {
+                            // TODO : the current parameter substitution is 
not expected to work well
+                            
queryBuilder.append(partialEscape(String.valueOf(pr.first.getValue(pr.first.getType())).replace('%',
 '*').replace('_', '?')));
+                        } else {
+                            throw new RuntimeException("[unexpected!] not 
handled case");
+                        }
                     }
                 }
                 queryBuilder.append(" ");
@@ -139,7 +168,7 @@ public class SolrQueryIndex implements Q
         for (String fulltextCondition : fulltextConditions) {
             queryBuilder.append(fulltextCondition).append(" ");
         }
-        if(queryBuilder.length() == 0) {
+        if (queryBuilder.length() == 0) {
             queryBuilder.append("*:*");
         }
         String escapedQuery = queryBuilder.toString();
@@ -198,6 +227,9 @@ public class SolrQueryIndex implements Q
                 log.debug("sending query {}", query);
             }
             QueryResponse queryResponse = solrServer.query(query);
+            if (log.isDebugEnabled()) {
+                log.debug("getting response {}", queryResponse);
+            }
             cursor = new SolrCursor(queryResponse);
         } catch (Exception e) {
             throw new RuntimeException(e);
@@ -250,4 +282,12 @@ public class SolrQueryIndex implements Q
             }
         }
     }
+
+
+    @Override
+    @CheckForNull
+    public NodeAggregator getNodeAggregator() {
+        return aggregator;
+    }
+    
 }

Modified: 
jackrabbit/oak/trunk/oak-solr-core/src/main/resources/solr/oak/conf/schema.xml
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-solr-core/src/main/resources/solr/oak/conf/schema.xml?rev=1572503&r1=1572502&r2=1572503&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-solr-core/src/main/resources/solr/oak/conf/schema.xml 
(original)
+++ 
jackrabbit/oak/trunk/oak-solr-core/src/main/resources/solr/oak/conf/schema.xml 
Thu Feb 27 10:44:08 2014
@@ -104,7 +104,7 @@
   </types>
   <fields>
     <field name="path_exact" type="string" indexed="true" stored="true"/>
-    <field name="path_child" type="children_path" indexed="true" 
stored="true"/>
+    <field name="path_child" type="children_path" indexed="true" 
stored="false"/>
     <field name="path_anc" type="parent_path" indexed="true" stored="false"/>
     <field name="path_des" type="descendent_path" indexed="true" 
stored="false"/>
     <field name="ignored" type="ignored" multiValued="true"/>

Modified: 
jackrabbit/oak/trunk/oak-solr-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrIndexQueryTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-solr-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrIndexQueryTest.java?rev=1572503&r1=1572502&r2=1572503&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-solr-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrIndexQueryTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-solr-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/solr/query/SolrIndexQueryTest.java
 Thu Feb 27 10:44:08 2014
@@ -162,4 +162,66 @@ public class SolrIndexQueryTest extends 
         assertEquals("/test/resource", strings.next());
         assertFalse(strings.hasNext());
     }
+
+    @Test
+    public void testNativeSolrQuery() throws Exception {
+        String nativeQueryString = "select [jcr:path] from [nt:base] where 
native('solr', 'name:(Hello OR World)')";
+
+        Tree tree = root.getTree("/");
+        Tree test = tree.addChild("test");
+        test.addChild("a").setProperty("name", "Hello");
+        test.addChild("b").setProperty("name", "World");
+        tree.addChild("c");
+        root.commit();
+
+        Iterator<String> strings = executeQuery(nativeQueryString, 
"JCR-SQL2").iterator();
+        assertTrue(strings.hasNext());
+        assertEquals("/test/a", strings.next());
+        assertTrue(strings.hasNext());
+        assertEquals("/test/b", strings.next());
+        assertFalse(strings.hasNext());
+    }
+
+    @Test
+    public void testNativeSolrFunctionQuery() throws Exception {
+        String nativeQueryString = "select [jcr:path] from [nt:base] where 
native('solr', 'path_child:\\/test  _val_:\"recip(rord(name),1,2,3)\"')";
+
+        Tree tree = root.getTree("/");
+        Tree test = tree.addChild("test");
+        test.addChild("a").setProperty("name", "Hello");
+        test.addChild("b").setProperty("name", "World");
+        tree.addChild("c");
+        root.commit();
+
+        Iterator<String> strings = executeQuery(nativeQueryString, 
"JCR-SQL2").iterator();
+        assertTrue(strings.hasNext());
+        assertEquals("/test/a", strings.next());
+        assertTrue(strings.hasNext());
+        assertEquals("/test/b", strings.next());
+        assertFalse(strings.hasNext());
+    }
+
+    @Test
+     public void testNativeSolrNestedQuery() throws Exception {
+        String nativeQueryString = "select [jcr:path] from [nt:base] where 
native('solr', '_query_:\"{!dismax qf=catch_all q.op=OR}hello world\"')";
+
+        Tree tree = root.getTree("/");
+        Tree test = tree.addChild("test");
+        test.addChild("a").setProperty("name", "Hello");
+        test.addChild("b").setProperty("name", "World");
+        tree.addChild("c");
+        root.commit();
+
+        Iterator<String> strings = executeQuery(nativeQueryString, 
"JCR-SQL2").iterator();
+        assertTrue(strings.hasNext());
+        assertEquals("/test/a", strings.next());
+        assertTrue(strings.hasNext());
+        assertEquals("/test/b", strings.next());
+        assertFalse(strings.hasNext());
+    }
+
+    @Test
+    public void nativeSolr() throws Exception {
+        test("native_solr.txt");
+    }
 }

Added: 
jackrabbit/oak/trunk/oak-solr-core/src/test/resources/org/apache/jackrabbit/oak/query/native_solr.txt
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-solr-core/src/test/resources/org/apache/jackrabbit/oak/query/native_solr.txt?rev=1572503&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-solr-core/src/test/resources/org/apache/jackrabbit/oak/query/native_solr.txt
 (added)
+++ 
jackrabbit/oak/trunk/oak-solr-core/src/test/resources/org/apache/jackrabbit/oak/query/native_solr.txt
 Thu Feb 27 10:44:08 2014
@@ -0,0 +1,48 @@
+# 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.
+#
+# Syntax:
+# * lines that start with spaces belong to the previous line
+# * lines starting with "#" are remarks.
+# * lines starting with "select" are queries, followed by expected results and 
an empty line
+# * lines starting with "explain" are followed by expected query plan and an 
empty line
+# * lines starting with "sql1" are run using the sql1 language
+# * lines starting with "xpath2sql" are just converted from xpath to sql2
+# * all other lines are are committed into the microkernel (line by line)
+# * new tests are typically be added on top, after the syntax docs
+# * use ascii character only
+
+commit / + "test": { "a": { "name": "Hello" }, "b": { "name" : "World" }}
+
+xpath //*[rep:native('solr', 'name:(Hello OR World)')]
+/test/a, null, null
+/test/b, null, null
+
+select [jcr:path] from [nt:base]
+  where native('solr', 'name:(Hello OR World)')
+/test/a
+/test/b
+
+select [jcr:path] from [nt:base] as a
+  where native(a, 'solr', 'path_child:\/test _val_:"recip(rord(name),1,2,3)"')
+/test/a
+/test/b
+
+select [jcr:path] from [nt:base]
+  where native('solr', 'path_child:\/test _val_:"recip(rord(name),1,2,3)"')
+/test/a
+/test/b
+
+

Modified: 
jackrabbit/oak/trunk/oak-solr-core/src/test/resources/solr/oak/conf/schema.xml
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-solr-core/src/test/resources/solr/oak/conf/schema.xml?rev=1572503&r1=1572502&r2=1572503&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-solr-core/src/test/resources/solr/oak/conf/schema.xml 
(original)
+++ 
jackrabbit/oak/trunk/oak-solr-core/src/test/resources/solr/oak/conf/schema.xml 
Thu Feb 27 10:44:08 2014
@@ -104,7 +104,7 @@
     </types>
     <fields>
         <field name="path_exact" type="string" indexed="true" stored="true"/>
-        <field name="path_child" type="children_path" indexed="true" 
stored="true"/>
+        <field name="path_child" type="children_path" indexed="true" 
stored="false"/>
         <field name="path_anc" type="parent_path" indexed="true" 
stored="false"/>
         <field name="path_des" type="descendent_path" indexed="true" 
stored="false"/>
         <field name="ignored" type="ignored" multiValued="true"/>


Reply via email to