You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by th...@apache.org on 2021/07/30 16:28:12 UTC

[lucene-solr] branch branch_8x updated: SOLR-15570: Include fields declared in the schema in table metadata (SQL) even if they are empty (#2539)

This is an automated email from the ASF dual-hosted git repository.

thelabdude pushed a commit to branch branch_8x
in repository https://gitbox.apache.org/repos/asf/lucene-solr.git


The following commit(s) were added to refs/heads/branch_8x by this push:
     new 1acb347  SOLR-15570: Include fields declared in the schema in table metadata (SQL) even if they are empty (#2539)
1acb347 is described below

commit 1acb34753bcb771586b8b4010417e79f80952205
Author: Timothy Potter <th...@gmail.com>
AuthorDate: Fri Jul 30 10:27:53 2021 -0600

    SOLR-15570: Include fields declared in the schema in table metadata (SQL) even if they are empty (#2539)
---
 solr/CHANGES.txt                                   |  2 ++
 .../org/apache/solr/handler/sql/SolrSchema.java    | 29 +++++++++++++---------
 .../org/apache/solr/handler/TestSQLHandler.java    | 16 ++++++++++++
 3 files changed, 35 insertions(+), 12 deletions(-)

diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 6fbcbf5..dcd6dd3 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -37,6 +37,8 @@ Improvements
 
 * SOLR-15549: ZkStateReader now supports connecting to 9.0 Solr Clouds (Houston Putman)
 
+* SOLR-15570: Include fields declared in the schema in table metadata (SQL) even if they are empty (Timothy Potter)
+
 Optimizations
 ---------------------
 * SOLR-15433: Replace transient core cache LRU by Caffeine cache. (Bruno Roustant)
diff --git a/solr/core/src/java/org/apache/solr/handler/sql/SolrSchema.java b/solr/core/src/java/org/apache/solr/handler/sql/SolrSchema.java
index 413bbf7..d527018 100644
--- a/solr/core/src/java/org/apache/solr/handler/sql/SolrSchema.java
+++ b/solr/core/src/java/org/apache/solr/handler/sql/SolrSchema.java
@@ -23,6 +23,8 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import com.google.common.collect.ImmutableMap;
 import org.apache.calcite.rel.type.RelDataType;
@@ -111,33 +113,40 @@ class SolrSchema extends AbstractSchema implements Closeable {
     }
   }
 
-  private Map<String, LukeResponse.FieldTypeInfo> getFieldTypeInfo(final String collection) {
+  private LukeResponse getSchema(final String collection) {
     final String zk = this.properties.getProperty("zk");
     PKIAuthenticationPlugin.withServerIdentity(true);
     try {
       LukeRequest lukeRequest = new LukeRequest();
-      lukeRequest.setShowSchema(true); // for custom type info ...
+      lukeRequest.setShowSchema(true); // for empty fields and custom type info ...
       lukeRequest.setNumTerms(0);
-      return lukeRequest.process(solrClientCache.getCloudSolrClient(zk), collection).getFieldTypeInfo();
+      return lukeRequest.process(solrClientCache.getCloudSolrClient(zk), collection);
     } catch (SolrServerException | IOException e) {
       throw new RuntimeException(e);
     } finally {
       PKIAuthenticationPlugin.withServerIdentity(false);
     }
   }
-
+  
   RelProtoDataType getRelDataType(String collection) {
     // Temporary type factory, just for the duration of this method. Allowable
     // because we're creating a proto-type, not a type; before being used, the
     // proto-type will be copied into a real type factory.
     final RelDataTypeFactory typeFactory = new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
     final RelDataTypeFactory.Builder fieldInfo = typeFactory.builder();
-    Map<String, LukeResponse.FieldInfo> luceneFieldInfoMap = getFieldInfo(collection);
+    
+    // Get fields that have data, including dynamic field instances
+    Map<String, LukeResponse.FieldInfo> fieldsInUseMap = getFieldInfo(collection);
+
+    LukeResponse schema = getSchema(collection);
+    // merge the actual fields in use returned by Luke with the declared fields in the schema that are empty
+    Map<String, LukeResponse.FieldInfo> combinedFields = Stream.of(fieldsInUseMap, schema.getFieldInfo())
+            .flatMap(map -> map.entrySet().stream())
+            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> v1));
 
-    Map<String, LukeResponse.FieldTypeInfo> fieldTypeInfoMap = null; // loaded lazily if needed
     Map<String, Class<?>> javaClassForTypeMap = new HashMap<>(); // local cache for custom field types we've already resolved
 
-    for (Map.Entry<String, LukeResponse.FieldInfo> entry : luceneFieldInfoMap.entrySet()) {
+    for (Map.Entry<String, LukeResponse.FieldInfo> entry : combinedFields.entrySet()) {
       LukeResponse.FieldInfo luceneFieldInfo = entry.getValue();
 
       String luceneFieldType = luceneFieldInfo.getType();
@@ -173,11 +182,7 @@ class SolrSchema extends AbstractSchema implements Closeable {
         default:
           Class<?> javaClass = javaClassForTypeMap.get(luceneFieldType);
           if (javaClass == null) {
-            if (fieldTypeInfoMap == null) {
-              // lazily go to luke for the field type info ...
-              fieldTypeInfoMap = getFieldTypeInfo(collection);
-            }
-            javaClass = guessJavaClassForFieldType(fieldTypeInfoMap.get(luceneFieldType));
+            javaClass = guessJavaClassForFieldType(schema.getFieldTypeInfo().get(luceneFieldType));
             javaClassForTypeMap.put(luceneFieldType, javaClass);
           }
           type = typeFactory.createJavaType(javaClass);
diff --git a/solr/core/src/test/org/apache/solr/handler/TestSQLHandler.java b/solr/core/src/test/org/apache/solr/handler/TestSQLHandler.java
index 06c9edd..136ebce 100644
--- a/solr/core/src/test/org/apache/solr/handler/TestSQLHandler.java
+++ b/solr/core/src/test/org/apache/solr/handler/TestSQLHandler.java
@@ -2281,4 +2281,20 @@ public class TestSQLHandler extends SolrCloudTestCase {
     // select * w/o limit is not supported by Solr SQL
     expectThrows(IOException.class, () -> expectResults("SELECT * FROM $ALIAS", -1));
   }
+
+  @Test
+  public void testSelectEmptyField() throws Exception {
+    new UpdateRequest()
+        .add("id", "01")
+        .add("id", "02")
+        .add("id", "03")
+        .add("id", "04")
+        .add("id", "05")
+        .commit(cluster.getSolrClient(), COLLECTIONORALIAS);
+
+    // stringx is declared in the schema but has no docs
+    expectResults("SELECT id, stringx FROM $ALIAS", 5);
+    // notafield_i matches a dynamic field pattern but has no docs, so don't allow this
+    expectThrows(IOException.class, () -> expectResults("SELECT id, stringx, notafield_i FROM $ALIAS", 5));
+  }
 }