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

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


The following commit(s) were added to refs/heads/master by this push:
     new 641e2b9e4d [IOTDB-4356] De-duplication of PathPatternTree  (#7289)
641e2b9e4d is described below

commit 641e2b9e4ddf580f3082eee0d0a8b36fb83e3d81
Author: Chen YZ <[email protected]>
AuthorDate: Tue Sep 13 14:06:42 2022 +0800

    [IOTDB-4356] De-duplication of PathPatternTree  (#7289)
    
    [IOTDB-4356] De-duplication of PathPatternTree #7289
---
 .../org/apache/iotdb/commons/path/PartialPath.java | 58 ++++++++++++++++++++++
 .../apache/iotdb/commons/path/PartialPathTest.java | 43 ++++++++++++++++
 .../db/mpp/common/schematree/PathPatternTree.java  | 12 +++--
 .../mpp/common/schematree/PathPatternTreeTest.java | 33 ++++++++++++
 4 files changed, 141 insertions(+), 5 deletions(-)

diff --git 
a/node-commons/src/main/java/org/apache/iotdb/commons/path/PartialPath.java 
b/node-commons/src/main/java/org/apache/iotdb/commons/path/PartialPath.java
index bec99ab45f..d3cae160db 100644
--- a/node-commons/src/main/java/org/apache/iotdb/commons/path/PartialPath.java
+++ b/node-commons/src/main/java/org/apache/iotdb/commons/path/PartialPath.java
@@ -356,6 +356,64 @@ public class PartialPath extends Path implements 
Comparable<Path>, Cloneable {
     return true;
   }
 
+  /**
+   * Test if this path pattern includes input path pattern. e.g. "root.**" 
includes "root.sg.**",
+   * "root.*.d.s" includes "root.sg.d.s", "root.sg.**" does not include 
"root.**.s", "root.*.d.s"
+   * does not include "root.sg.d1.*"
+   *
+   * @param rPath a pattern path of a timeseries
+   * @return true if this path pattern includes input path pattern, otherwise 
return false
+   */
+  public boolean include(PartialPath rPath) {
+    String[] rNodes = rPath.getNodes();
+    String[] lNodes = nodes.clone();
+    // Replace * with ** if they are adjacent to each other
+    for (int i = 1; i < lNodes.length; i++) {
+      if (MULTI_LEVEL_PATH_WILDCARD.equals(lNodes[i - 1])
+          && ONE_LEVEL_PATH_WILDCARD.equals(lNodes[i])) {
+        lNodes[i] = MULTI_LEVEL_PATH_WILDCARD;
+      }
+      if (MULTI_LEVEL_PATH_WILDCARD.equals(lNodes[lNodes.length - i])
+          && ONE_LEVEL_PATH_WILDCARD.equals(lNodes[lNodes.length - 1 - i])) {
+        lNodes[lNodes.length - 1 - i] = MULTI_LEVEL_PATH_WILDCARD;
+      }
+    }
+    // dp[i][j] means if nodes1[0:i) includes nodes[0:j)
+    // for example: "root.sg.**" includes "root.sg.d1.*"
+    // 1 0 0 0 0 |→| 1 0 0 0 0 |→| 1 0 0 0 0 |→| 1 0 0 0 0
+    // 0 0 0 0 0 |↓| 0 1 0 0 0 |→| 0 1 0 0 0 |→| 0 1 0 0 0
+    // 0 0 0 0 0 |↓| 0 0 0 0 0 |↓| 0 0 1 0 0 |→| 0 0 1 0 0
+    // 0 0 0 0 0 |↓| 0 0 0 0 0 |↓| 0 0 0 0 0 |↓| 0 0 0 1 1
+    // Since the derivation of the next row depends only on the previous row, 
the calculation can
+    // be performed using a one-dimensional array
+    boolean[] dp = new boolean[rNodes.length + 1];
+    dp[0] = true;
+    for (int i = 1; i <= lNodes.length; i++) {
+      boolean[] newDp = new boolean[rNodes.length + 1];
+      for (int j = i; j <= rNodes.length; j++) {
+        if (lNodes[i - 1].equals(MULTI_LEVEL_PATH_WILDCARD)) {
+          // if encounter MULTI_LEVEL_PATH_WILDCARD
+          if (dp[j - 1]) {
+            for (int k = j; k <= rNodes.length; k++) {
+              newDp[k] = true;
+            }
+            break;
+          }
+        } else {
+          // if without MULTI_LEVEL_PATH_WILDCARD, scan and check
+          if (!rNodes[j - 1].equals(MULTI_LEVEL_PATH_WILDCARD)
+              && (lNodes[i - 1].equals(ONE_LEVEL_PATH_WILDCARD)
+                  || lNodes[i - 1].equals(rNodes[j - 1]))) {
+            // if nodes1[i-1] includes rNodes[j-1], dp[i][j] = dp[i-1][j-1]
+            newDp[j] |= dp[j - 1];
+          }
+        }
+      }
+      dp = newDp;
+    }
+    return dp[rNodes.length];
+  }
+
   /**
    * Test if this path pattern overlaps with input path pattern. Overlap means 
the result sets
    * generated by two path pattern share some common elements. e.g. 
"root.sg.**" overlaps with
diff --git 
a/node-commons/src/test/java/org/apache/iotdb/commons/path/PartialPathTest.java 
b/node-commons/src/test/java/org/apache/iotdb/commons/path/PartialPathTest.java
index 273e7e12c2..1b606dc210 100644
--- 
a/node-commons/src/test/java/org/apache/iotdb/commons/path/PartialPathTest.java
+++ 
b/node-commons/src/test/java/org/apache/iotdb/commons/path/PartialPathTest.java
@@ -699,6 +699,49 @@ public class PartialPathTest {
     }
   }
 
+  @Test
+  public void testInclude() throws IllegalPathException {
+    PartialPath[][] pathPairs =
+        new PartialPath[][] {
+          new PartialPath[] {new PartialPath("root.**"), new 
PartialPath("root.sg.**")},
+          new PartialPath[] {new PartialPath("root.**.*"), new 
PartialPath("root.**")},
+          new PartialPath[] {new PartialPath("root.**.*"), new 
PartialPath("root.sg.**")},
+          new PartialPath[] {new PartialPath("root.**.s"), new 
PartialPath("root.sg.**")},
+          new PartialPath[] {new PartialPath("root.*.**"), new 
PartialPath("root.sg.**")},
+          new PartialPath[] {new PartialPath("root.*.d.s"), new 
PartialPath("root.sg1.d.s")},
+          new PartialPath[] {new PartialPath("root.**.s"), new 
PartialPath("root.*.d.s")},
+          new PartialPath[] {new PartialPath("root.*.d.s"), new 
PartialPath("root.**.s")},
+          new PartialPath[] {new PartialPath("root.*.d.s"), new 
PartialPath("root.sg.*.s")},
+          new PartialPath[] {new PartialPath("root.*.d.s"), new 
PartialPath("root.sg.d2.s")},
+          new PartialPath[] {new PartialPath("root.*.d.s.*"), new 
PartialPath("root.sg.d.s")},
+          new PartialPath[] {new PartialPath("root.**.d.s"), new 
PartialPath("root.**.d2.s")},
+          new PartialPath[] {new PartialPath("root.**.*.s"), new 
PartialPath("root.**.d2.s")},
+          new PartialPath[] {new PartialPath("root.**.d1.*"), new 
PartialPath("root.*")},
+          new PartialPath[] {new PartialPath("root.**.d1.*"), new 
PartialPath("root.d2.*.s")},
+          new PartialPath[] {new PartialPath("root.**.d1.**"), new 
PartialPath("root.d2.**")},
+          new PartialPath[] {
+            new PartialPath("root.**.*.**.**"), new 
PartialPath("root.d2.*.s1.**")
+          },
+          new PartialPath[] {new PartialPath("root.**.s1.d1"), new 
PartialPath("root.s1.d1.**")},
+          new PartialPath[] {new PartialPath("root.**.s1"), new 
PartialPath("root.**.s2.s1")},
+          new PartialPath[] {
+            new PartialPath("root.**.s1.s2.**"), new 
PartialPath("root.d1.s1.s2.*")
+          },
+          new PartialPath[] {new PartialPath("root.**.s1"), new 
PartialPath("root.**.s2")},
+          new PartialPath[] {new PartialPath("root.*.*.**"), new 
PartialPath("root.**.*")},
+          new PartialPath[] {new PartialPath("root.**.**"), new 
PartialPath("root.*.**.**.*")},
+        };
+    boolean[] results =
+        new boolean[] {
+          true, false, true, false, true, true, true, false, false, false, 
false, false, true,
+          false, false, false, true, false, true, true, false, false, true
+        };
+    Assert.assertEquals(pathPairs.length, results.length);
+    for (int i = 0; i < pathPairs.length; i++) {
+      Assert.assertEquals(results[i], 
pathPairs[i][0].include(pathPairs[i][1]));
+    }
+  }
+
   private void checkNodes(String[] expected, String[] actual) {
     Assert.assertEquals(expected.length, actual.length);
     for (int i = 0; i < expected.length; i++) {
diff --git 
a/server/src/main/java/org/apache/iotdb/db/mpp/common/schematree/PathPatternTree.java
 
b/server/src/main/java/org/apache/iotdb/db/mpp/common/schematree/PathPatternTree.java
index ef0e7a4e97..df8b8f3b35 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/mpp/common/schematree/PathPatternTree.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/mpp/common/schematree/PathPatternTree.java
@@ -33,8 +33,10 @@ import java.nio.ByteBuffer;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Deque;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 public class PathPatternTree {
@@ -79,7 +81,7 @@ public class PathPatternTree {
   public void appendPathPattern(PartialPath pathPattern) {
     boolean isExist = false;
     for (PartialPath path : pathPatternList) {
-      if (path.matchFullPath(pathPattern)) {
+      if (path.include(pathPattern)) {
         // path already exists in pathPatternList
         isExist = true;
         break;
@@ -88,7 +90,7 @@ public class PathPatternTree {
     if (!isExist) {
       // remove duplicate path in pathPatternList
       pathPatternList.removeAll(
-          
pathPatternList.stream().filter(pathPattern::matchFullPath).collect(Collectors.toList()));
+          
pathPatternList.stream().filter(pathPattern::include).collect(Collectors.toList()));
       pathPatternList.add(pathPattern);
     }
   }
@@ -134,13 +136,13 @@ public class PathPatternTree {
 
   public List<String> getAllDevicePatterns() {
     List<String> nodes = new ArrayList<>();
-    List<String> results = new ArrayList<>();
+    Set<String> results = new HashSet<>();
     searchDevicePattern(root, nodes, results);
-    return results;
+    return new ArrayList<>(results);
   }
 
   private void searchDevicePattern(
-      PathPatternNode curNode, List<String> nodes, List<String> results) {
+      PathPatternNode curNode, List<String> nodes, Set<String> results) {
     nodes.add(curNode.getName());
     if (curNode.isLeaf()) {
       if (!curNode.getName().equals(IoTDBConstant.MULTI_LEVEL_PATH_WILDCARD)) {
diff --git 
a/server/src/test/java/org/apache/iotdb/db/mpp/common/schematree/PathPatternTreeTest.java
 
b/server/src/test/java/org/apache/iotdb/db/mpp/common/schematree/PathPatternTreeTest.java
index 614430f4fb..eff740e1b2 100644
--- 
a/server/src/test/java/org/apache/iotdb/db/mpp/common/schematree/PathPatternTreeTest.java
+++ 
b/server/src/test/java/org/apache/iotdb/db/mpp/common/schematree/PathPatternTreeTest.java
@@ -136,6 +136,35 @@ public class PathPatternTreeTest {
             new PartialPath("root.sg1.d3.t1")));
   }
 
+  /** This use case is used to test the de-duplication of getAllPathPatterns 
results */
+  @Test
+  public void pathPatternTreeTest7() throws IllegalPathException, IOException {
+    checkPathPatternTree(
+        Arrays.asList(
+            new PartialPath("root.sg1.d1.s1"),
+            new PartialPath("root.sg1.*.s2"),
+            new PartialPath("root.sg1.d1.t1.s1"),
+            new PartialPath("root.sg1.*.s1"),
+            new PartialPath("root.sg1.**.s1")),
+        Arrays.asList(new PartialPath("root.sg1.*.s2"), new 
PartialPath("root.sg1.**.s1")),
+        Arrays.asList(new PartialPath("root.sg1.*"), new 
PartialPath("root.sg1.**")));
+  }
+
+  /** This use case is used to test the de-duplication of getAllDevicePatterns 
results */
+  @Test
+  public void pathPatternTreeTest8() throws IllegalPathException, IOException {
+    checkPathPatternTree(
+        Arrays.asList(new PartialPath("root.sg1.d1.s1"), new 
PartialPath("root.sg1.d1.s2")),
+        Arrays.asList(new PartialPath("root.sg1.d1.s1"), new 
PartialPath("root.sg1.d1.s2")),
+        Collections.singletonList(new PartialPath("root.sg1.d1")));
+  }
+
+  /**
+   * @param paths PartialPath list to create PathPatternTree
+   * @param compressedPaths Expected PartialPath list of getAllPathPatterns
+   * @param compressedDevicePaths Expected PartialPath list of 
getAllDevicePatterns
+   * @throws IOException
+   */
   private void checkPathPatternTree(
       List<PartialPath> paths,
       List<PartialPath> compressedPaths,
@@ -147,6 +176,10 @@ public class PathPatternTreeTest {
     }
     patternTree.constructTree();
 
+    Assert.assertEquals(
+        compressedPaths.stream().sorted().collect(Collectors.toList()),
+        
patternTree.getAllPathPatterns().stream().sorted().collect(Collectors.toList()));
+
     PathPatternTree resultPatternTree = new PathPatternTree();
     for (PartialPath path : compressedPaths) {
       resultPatternTree.appendPathPattern(path);

Reply via email to