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

sumitagrawal pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/master by this push:
     new 2e30dc182c HDDS-11190. Add --fields option to ldb scan command (#6976)
2e30dc182c is described below

commit 2e30dc182c9c3e50b0c023f5a4915fa6a88e5eb8
Author: Tejaskriya <[email protected]>
AuthorDate: Tue Aug 27 15:27:39 2024 +0530

    HDDS-11190. Add --fields option to ldb scan command (#6976)
---
 .../org/apache/hadoop/ozone/debug/DBScanner.java   | 122 ++++++++++++++++++++-
 .../org/apache/hadoop/ozone/debug/ValueSchema.java |  17 +--
 2 files changed, 126 insertions(+), 13 deletions(-)

diff --git 
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/DBScanner.java 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/DBScanner.java
index 0c38fbe33b..4653aa3eeb 100644
--- 
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/DBScanner.java
+++ 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/DBScanner.java
@@ -55,9 +55,11 @@ import picocli.CommandLine;
 import java.io.BufferedWriter;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.lang.reflect.Field;
 import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -121,6 +123,11 @@ public class DBScanner implements Callable<Void>, 
SubcommandWithParent {
       description = "Key at which iteration of the DB ends")
   private String endKey;
 
+  @CommandLine.Option(names = {"--fields"},
+      description = "Comma-separated list of fields needed for each value. " +
+          "eg.) \"name,acls.type\" for showing name and type under acls.")
+  private String fieldsFilter;
+
   @CommandLine.Option(names = {"--dnSchema", "--dn-schema", "-d"},
       description = "Datanode DB Schema Version: V1/V2/V3",
       defaultValue = "V3")
@@ -291,7 +298,7 @@ public class DBScanner implements Callable<Void>, 
SubcommandWithParent {
         }
         Future<Void> future = threadPool.submit(
             new Task(dbColumnFamilyDef, batch, logWriter, sequenceId,
-                withKey, schemaV3));
+                withKey, schemaV3, fieldsFilter));
         futures.add(future);
         batch = new ArrayList<>(batchSize);
         sequenceId++;
@@ -299,7 +306,7 @@ public class DBScanner implements Callable<Void>, 
SubcommandWithParent {
     }
     if (!batch.isEmpty()) {
       Future<Void> future = threadPool.submit(new Task(dbColumnFamilyDef,
-          batch, logWriter, sequenceId, withKey, schemaV3));
+          batch, logWriter, sequenceId, withKey, schemaV3, fieldsFilter));
       futures.add(future);
     }
 
@@ -465,22 +472,51 @@ public class DBScanner implements Callable<Void>, 
SubcommandWithParent {
     private final long sequenceId;
     private final boolean withKey;
     private final boolean schemaV3;
+    private String valueFields;
 
     Task(DBColumnFamilyDefinition dbColumnFamilyDefinition,
          ArrayList<ByteArrayKeyValue> batch, LogWriter logWriter,
-         long sequenceId, boolean withKey, boolean schemaV3) {
+         long sequenceId, boolean withKey, boolean schemaV3, String 
valueFields) {
       this.dbColumnFamilyDefinition = dbColumnFamilyDefinition;
       this.batch = batch;
       this.logWriter = logWriter;
       this.sequenceId = sequenceId;
       this.withKey = withKey;
       this.schemaV3 = schemaV3;
+      this.valueFields = valueFields;
+    }
+
+    Map<String, Object> getFieldSplit(List<String> fields, Map<String, Object> 
fieldMap) {
+      int len = fields.size();
+      if (fieldMap == null) {
+        fieldMap = new HashMap<>();
+      }
+      if (len == 1) {
+        fieldMap.putIfAbsent(fields.get(0), null);
+      } else {
+        Map<String, Object> fieldMapGet = (Map<String, Object>) 
fieldMap.get(fields.get(0));
+        if (fieldMapGet == null) {
+          fieldMap.put(fields.get(0), getFieldSplit(fields.subList(1, len), 
null));
+        } else {
+          fieldMap.put(fields.get(0), getFieldSplit(fields.subList(1, len), 
fieldMapGet));
+        }
+      }
+      return fieldMap;
     }
 
     @Override
     public Void call() {
       try {
         ArrayList<String> results = new ArrayList<>(batch.size());
+        Map<String, Object> fieldsSplitMap = new HashMap<>();
+
+        if (valueFields != null) {
+          for (String field : valueFields.split(",")) {
+            String[] subfields = field.split("\\.");
+            fieldsSplitMap = getFieldSplit(Arrays.asList(subfields), 
fieldsSplitMap);
+          }
+        }
+
         for (ByteArrayKeyValue byteArrayKeyValue : batch) {
           StringBuilder sb = new StringBuilder();
           if (!(sequenceId == FIRST_SEQUENCE_ID && results.isEmpty())) {
@@ -515,16 +551,92 @@ public class DBScanner implements Callable<Void>, 
SubcommandWithParent {
 
           Object o = dbColumnFamilyDefinition.getValueCodec()
               .fromPersistedFormat(byteArrayKeyValue.getValue());
-          sb.append(WRITER.writeValueAsString(o));
+
+          if (valueFields != null) {
+            Map<String, Object> filteredValue = new HashMap<>();
+            filteredValue.putAll(getFilteredObject(o, 
dbColumnFamilyDefinition.getValueType(), fieldsSplitMap));
+            sb.append(WRITER.writeValueAsString(filteredValue));
+          } else {
+            sb.append(WRITER.writeValueAsString(o));
+          }
+
           results.add(sb.toString());
         }
         logWriter.log(results, sequenceId);
-      } catch (Exception e) {
+      } catch (IOException e) {
         exception = true;
         LOG.error("Exception parse Object", e);
       }
       return null;
     }
+
+    Map<String, Object> getFilteredObject(Object obj, Class<?> clazz, 
Map<String, Object> fieldsSplitMap) {
+      Map<String, Object> valueMap = new HashMap<>();
+      for (Map.Entry<String, Object> field : fieldsSplitMap.entrySet()) {
+        try {
+          Field valueClassField = getRequiredFieldFromAllFields(clazz, 
field.getKey());
+          Object valueObject = valueClassField.get(obj);
+          Map<String, Object> subfields = (Map<String, Object>) 
field.getValue();
+
+          if (subfields == null) {
+            valueMap.put(field.getKey(), valueObject);
+          } else {
+            if (Collection.class.isAssignableFrom(valueObject.getClass())) {
+              List<Object> subfieldObjectsList =
+                  getFilteredObjectCollection((Collection) valueObject, 
subfields);
+              valueMap.put(field.getKey(), subfieldObjectsList);
+            } else if (Map.class.isAssignableFrom(valueObject.getClass())) {
+              Map<Object, Object> subfieldObjectsMap = new HashMap<>();
+              Map<?, ?> valueObjectMap = (Map<?, ?>) valueObject;
+              for (Map.Entry<?, ?> ob : valueObjectMap.entrySet()) {
+                Object subfieldValue;
+                if 
(Collection.class.isAssignableFrom(ob.getValue().getClass())) {
+                  subfieldValue = 
getFilteredObjectCollection((Collection)ob.getValue(), subfields);
+                } else {
+                  subfieldValue = getFilteredObject(ob.getValue(), 
ob.getValue().getClass(), subfields);
+                }
+                subfieldObjectsMap.put(ob.getKey(), subfieldValue);
+              }
+              valueMap.put(field.getKey(), subfieldObjectsMap);
+            } else {
+              valueMap.put(field.getKey(),
+                  getFilteredObject(valueObject, valueClassField.getType(), 
subfields));
+            }
+          }
+        } catch (NoSuchFieldException ex) {
+          err().println("ERROR: no such field: " + field);
+        } catch (IllegalAccessException e) {
+          err().println("ERROR: Cannot get field from object: " + field);
+        }
+      }
+      return valueMap;
+    }
+
+    List<Object> getFilteredObjectCollection(Collection<?> valueObject, 
Map<String, Object> fields)
+        throws NoSuchFieldException, IllegalAccessException {
+      List<Object> subfieldObjectsList = new ArrayList<>();
+      for (Object ob : valueObject) {
+        Object subfieldValue = getFilteredObject(ob, ob.getClass(), fields);
+        subfieldObjectsList.add(subfieldValue);
+      }
+      return subfieldObjectsList;
+    }
+
+    Field getRequiredFieldFromAllFields(Class clazz, String fieldName) throws 
NoSuchFieldException {
+      List<Field> classFieldList = ValueSchema.getAllFields(clazz);
+      Field classField = null;
+      for (Field f : classFieldList) {
+        if (f.getName().equals(fieldName)) {
+          classField = f;
+          break;
+        }
+      }
+      if (classField == null) {
+        throw new NoSuchFieldException();
+      }
+      classField.setAccessible(true);
+      return classField;
+    }
   }
 
   private static class ByteArrayKeyValue {
diff --git 
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/ValueSchema.java
 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/ValueSchema.java
index a5029b3e6b..b06be2aff5 100644
--- 
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/ValueSchema.java
+++ 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/debug/ValueSchema.java
@@ -88,7 +88,7 @@ public class ValueSchema implements Callable<Void>, 
SubcommandWithParent {
 
     String dbPath = parent.getDbPath();
     Map<String, Object> fields = new HashMap<>();
-    success = getValueFields(dbPath, fields);
+    success = getValueFields(dbPath, fields, depth, tableName, 
dnDBSchemaVersion);
 
     out().println(JsonUtils.toJsonStringWithDefaultPrettyPrinter(fields));
 
@@ -101,7 +101,8 @@ public class ValueSchema implements Callable<Void>, 
SubcommandWithParent {
     return null;
   }
 
-  private boolean getValueFields(String dbPath, Map<String, Object> 
valueSchema) {
+  public static boolean getValueFields(String dbPath, Map<String, Object> 
valueSchema, int d, String table,
+                                       String dnDBSchemaVersion) {
 
     dbPath = removeTrailingSlashIfNeeded(dbPath);
     DBDefinitionFactory.setDnDBSchemaVersion(dnDBSchemaVersion);
@@ -111,19 +112,19 @@ public class ValueSchema implements Callable<Void>, 
SubcommandWithParent {
       return false;
     }
     final DBColumnFamilyDefinition<?, ?> columnFamilyDefinition =
-        dbDefinition.getColumnFamily(tableName);
+        dbDefinition.getColumnFamily(table);
     if (columnFamilyDefinition == null) {
-      err().print("Error: Table with name '" + tableName + "' not found");
+      err().print("Error: Table with name '" + table + "' not found");
       return false;
     }
 
     Class<?> c = columnFamilyDefinition.getValueType();
-    valueSchema.put(c.getSimpleName(), getFieldsStructure(c, depth));
+    valueSchema.put(c.getSimpleName(), getFieldsStructure(c, d));
 
     return true;
   }
 
-  private Object getFieldsStructure(Class<?> clazz, int currentDepth) {
+  private static Object getFieldsStructure(Class<?> clazz, int currentDepth) {
     if (clazz.isPrimitive() || String.class.equals(clazz)) {
       return clazz.getSimpleName();
     } else if (currentDepth == 0) {
@@ -148,7 +149,7 @@ public class ValueSchema implements Callable<Void>, 
SubcommandWithParent {
     }
   }
 
-  private List<Field> getAllFields(Class clazz) {
+  public static List<Field> getAllFields(Class clazz) {
     // NOTE: Schema of interface type, like ReplicationConfig, cannot be 
fetched.
     //       An empty list "[]" will be shown for such types of fields.
     if (clazz == null) {
@@ -176,7 +177,7 @@ public class ValueSchema implements Callable<Void>, 
SubcommandWithParent {
     return RDBParser.class;
   }
 
-  private String removeTrailingSlashIfNeeded(String dbPath) {
+  private static String removeTrailingSlashIfNeeded(String dbPath) {
     if (dbPath.endsWith(OzoneConsts.OZONE_URI_DELIMITER)) {
       dbPath = dbPath.substring(0, dbPath.length() - 1);
     }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to