This is an automated email from the ASF dual-hosted git repository.
chenyz pushed a commit to branch rel/1.2
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/rel/1.2 by this push:
new 47dc350609c Implement intersect with prefix pattern for PartialPath
and PathPatternTree (#10909)
47dc350609c is described below
commit 47dc350609cba837e94c676c378c018a012c377a
Author: Chen YZ <[email protected]>
AuthorDate: Mon Aug 21 09:06:12 2023 +0800
Implement intersect with prefix pattern for PartialPath and PathPatternTree
(#10909)
---
.../org/apache/iotdb/commons/path/PartialPath.java | 158 +++++++++++++++------
.../iotdb/commons/path/PathPatternTreeUtils.java | 53 +++++++
.../apache/iotdb/commons/path/PathPatternUtil.java | 4 +-
.../apache/iotdb/commons/path/PartialPathTest.java | 68 +++++++++
.../iotdb/commons/path/PathPatternTreeTest.java | 121 ++++++++++++++++
5 files changed, 362 insertions(+), 42 deletions(-)
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/path/PartialPath.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/path/PartialPath.java
index af0d037df33..1e7bbb0a324 100644
---
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/path/PartialPath.java
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/path/PartialPath.java
@@ -29,6 +29,7 @@ import org.apache.iotdb.tsfile.utils.PublicBAOS;
import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils;
import org.apache.iotdb.tsfile.write.schema.IMeasurementSchema;
+import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -52,6 +53,7 @@ import static
org.apache.iotdb.commons.conf.IoTDBConstant.ONE_LEVEL_PATH_WILDCAR
public class PartialPath extends Path implements Comparable<Path>, Cloneable {
private static final Logger logger =
LoggerFactory.getLogger(PartialPath.class);
+ private static final PartialPath ALL_MATCH_PATTERN = new PartialPath(new
String[] {"root", "**"});
protected String[] nodes;
@@ -116,14 +118,17 @@ public class PartialPath extends Path implements
Comparable<Path>, Cloneable {
public boolean hasMultiLevelMatchWildcard() {
for (String node : nodes) {
- // *, ** , d*, *d*
- if (PathPatternUtil.hasMultiLevelMatchWildcard(node)) {
+ if (PathPatternUtil.isMultiLevelMatchWildcard(node)) {
return true;
}
}
return false;
}
+ public boolean endWithMultiLevelWildcard() {
+ return PathPatternUtil.isMultiLevelMatchWildcard(nodes[nodes.length - 1]);
+ }
+
// e.g. root.db.d.s, root.db.d.*, root.db.d.s*, not include patterns like
root.db.d.**
public boolean hasExplicitDevice() {
if (nodes[nodes.length - 1].equals(MULTI_LEVEL_PATH_WILDCARD)) {
@@ -492,6 +497,62 @@ public class PartialPath extends Path implements
Comparable<Path>, Cloneable {
return this.nodes.length == rNodes.length;
}
+ /**
+ * Try to check overlap between nodes1 and nodes2 with
MULTI_LEVEL_PATH_WILDCARD. Time complexity
+ * O(n^2).
+ *
+ * @return true if overlapping, otherwise return false
+ */
+ private boolean checkOverlapWithMultiLevelWildcard(String[] nodes1, String[]
nodes2) {
+ // dp[i][j] means if nodes1[0:i) and nodes[0:j) overlapping
+ boolean[][] dp = new boolean[nodes1.length + 1][nodes2.length + 1];
+ dp[0][0] = true;
+ for (int i = 1; i <= nodes1.length; i++) {
+ boolean prune = false; //
+ // prune if we've already found that these two path are never overlapped
in the beginning
+ // steps.
+ // e.g. root.db1.**.s1 and root.db2.**.s1
+ for (int j = 1; j <= nodes2.length; j++) {
+ if (nodes1[i - 1].equals(MULTI_LEVEL_PATH_WILDCARD)
+ || nodes2[j - 1].equals(MULTI_LEVEL_PATH_WILDCARD)) {
+ // if encounter MULTI_LEVEL_PATH_WILDCARD
+ if (nodes1[i - 1].equals(MULTI_LEVEL_PATH_WILDCARD)) {
+ // if nodes1[i-1] is MULTI_LEVEL_PATH_WILDCARD,
dp[i][k(k>=j)]=dp[i-1][j-1]
+ if (dp[i - 1][j - 1]) {
+ for (int k = j; k <= nodes2.length; k++) {
+ dp[i][k] = true;
+ }
+ }
+ }
+ if (nodes2[j - 1].equals(MULTI_LEVEL_PATH_WILDCARD)) {
+ // if nodes2[j-1] is MULTI_LEVEL_PATH_WILDCARD,
dp[k(k>=i)][j]=dp[i-1][j-1]
+ if (dp[i - 1][j - 1]) {
+ for (int k = i; k <= nodes1.length; k++) {
+ dp[k][j] = true;
+ }
+ }
+ }
+ } else {
+ // if without MULTI_LEVEL_PATH_WILDCARD, scan and check
+ if ((PathPatternUtil.hasWildcard(nodes1[i - 1])
+ && PathPatternUtil.isNodeMatch(nodes1[i - 1], nodes2[j - 1]))
+ || (PathPatternUtil.hasWildcard(nodes2[j - 1])
+ && PathPatternUtil.isNodeMatch(nodes2[j - 1], nodes1[i - 1]))
+ || nodes1[i - 1].equals(nodes2[j - 1])) {
+ // if nodes1[i-1] and nodes[j-1] is matched, dp[i][j] =
dp[i-1][j-1]
+ dp[i][j] |= dp[i - 1][j - 1];
+ }
+ }
+ prune |= dp[i][j];
+ }
+ if (!prune) {
+ break;
+ }
+ }
+
+ return dp[nodes1.length][nodes2.length];
+ }
+
/**
* Test if this path pattern overlaps with input prefix full path. Overlap
means the result sets
* generated by two path pattern share some common elements. e.g.
@@ -533,51 +594,68 @@ public class PartialPath extends Path implements
Comparable<Path>, Cloneable {
}
/**
- * Try to check overlap between nodes1 and nodes2 with
MULTI_LEVEL_PATH_WILDCARD. Time complexity
- * O(n^2).
+ * Generate a list of partial paths which are the intersection of this path
pattern and input
+ * prefix pattern.
*
- * @return true if overlapping, otherwise return false
+ * @param prefixPattern must be a prefix full path ending with one "**",
like "root.sg.**"
+ * @return a list of partial paths which are the intersection of this path
pattern and input
+ * prefix pattern.
*/
- private boolean checkOverlapWithMultiLevelWildcard(String[] nodes1, String[]
nodes2) {
- // dp[i][j] means if nodes1[0:i) and nodes[0:j) overlapping
- boolean[][] dp = new boolean[nodes1.length + 1][nodes2.length + 1];
- dp[0][0] = true;
- for (int i = 1; i <= nodes1.length; i++) {
- for (int j = 1; j <= nodes2.length; j++) {
- if (nodes1[i - 1].equals(MULTI_LEVEL_PATH_WILDCARD)
- || nodes2[j - 1].equals(MULTI_LEVEL_PATH_WILDCARD)) {
- // if encounter MULTI_LEVEL_PATH_WILDCARD
- if (nodes1[i - 1].equals(MULTI_LEVEL_PATH_WILDCARD)) {
- // if nodes1[i-1] is MULTI_LEVEL_PATH_WILDCARD,
dp[i][k(k>=j)]=dp[i-1][j-1]
- if (dp[i - 1][j - 1]) {
- for (int k = j; k <= nodes2.length; k++) {
- dp[i][k] = true;
- }
- }
- }
- if (nodes2[j - 1].equals(MULTI_LEVEL_PATH_WILDCARD)) {
- // if nodes2[j-1] is MULTI_LEVEL_PATH_WILDCARD,
dp[k(k>=i)][j]=dp[i-1][j-1]
- if (dp[i - 1][j - 1]) {
- for (int k = i; k <= nodes1.length; k++) {
- dp[k][j] = true;
- }
- }
- }
+ public List<PartialPath> intersectWithPrefixPattern(PartialPath
prefixPattern) {
+ if (prefixPattern.equals(ALL_MATCH_PATTERN)) {
+ return Collections.singletonList(this);
+ }
+ String[] prefixFullPath =
+ Arrays.copyOf(prefixPattern.getNodes(), prefixPattern.getNodeLength()
- 1);
+ // init dp array
+ int thisLength = this.getNodeLength();
+ boolean[] matchIndex = new boolean[thisLength];
+ matchIndex[0] = true; // "root" must match "root"
+
+ // dp[i][j] means if nodes[0:i] matches prefixFullPath[0:j]
+ // for example: "root.**.d1.**" intersect "root.sg1.d1(.**)"
+ // dp[i][j] = (nodes[i]=="**"&&dp[i][j-1]) || (nodes[i] matches
prefixFullPath[j]&&dp[i-1][j-1])
+ // 1 0 0 0 |→| 1 0 0 0 |→| 1 0 0 0
+ // 0 0 0 0 |↓| 0 1 0 0 |→| 0 1 0 0
+ // 0 0 0 0 |↓| 0 0 0 0 |↓| 0 1 1 0
+ // Since the derivation of the next row depends only on the previous row,
the calculation can
+ // be performed using a one-dimensional array named "matchIndex"
+ for (int i = 1; i < prefixFullPath.length; i++) {
+ for (int j = thisLength - 1; j >= 1; j--) {
+ if (nodes[j].equals(MULTI_LEVEL_PATH_WILDCARD)) {
+ matchIndex[j] = matchIndex[j] || matchIndex[j - 1];
+ } else if (PathPatternUtil.isNodeMatch(nodes[j], prefixFullPath[i])) {
+ matchIndex[j] = matchIndex[j - 1];
} else {
- // if without MULTI_LEVEL_PATH_WILDCARD, scan and check
- if ((PathPatternUtil.hasWildcard(nodes1[i - 1])
- && PathPatternUtil.isNodeMatch(nodes1[i - 1], nodes2[j - 1]))
- || (PathPatternUtil.hasWildcard(nodes2[j - 1])
- && PathPatternUtil.isNodeMatch(nodes2[j - 1], nodes1[i - 1]))
- || nodes1[i - 1].equals(nodes2[j - 1])) {
- // if nodes1[i-1] and nodes[2] is matched, dp[i][j] = dp[i-1][j-1]
- dp[i][j] |= dp[i - 1][j - 1];
+ matchIndex[j] = false;
+ }
+ }
+ }
+ // Scan in reverse order to construct the result set.
+ // The structure of the result set is prefixFullPath+remaining nodes.
【E.g.root.sg1.d1 + **】
+ // It can be pruned if the remaining nodes start with **.
+ List<PartialPath> res = new ArrayList<>();
+ if (matchIndex[thisLength - 1] && nodes[thisLength -
1].equals(MULTI_LEVEL_PATH_WILDCARD)) {
+ res.add(new PartialPath(ArrayUtils.addAll(prefixFullPath,
nodes[thisLength - 1])));
+ } else {
+ for (int j = thisLength - 2; j > 0; j--) {
+ if (matchIndex[j]) {
+ res.add(
+ new PartialPath(
+ ArrayUtils.addAll(prefixFullPath, Arrays.copyOfRange(nodes,
j + 1, thisLength))));
+ if (nodes[j + 1].equals(MULTI_LEVEL_PATH_WILDCARD)) {
+ break;
+ }
+ if (nodes[j].equals(MULTI_LEVEL_PATH_WILDCARD)) {
+ res.add(
+ new PartialPath(
+ ArrayUtils.addAll(prefixFullPath,
Arrays.copyOfRange(nodes, j, thisLength))));
+ break;
}
}
}
}
-
- return dp[nodes1.length][nodes2.length];
+ return res;
}
@Override
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/path/PathPatternTreeUtils.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/path/PathPatternTreeUtils.java
new file mode 100644
index 00000000000..563cedff6f6
--- /dev/null
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/path/PathPatternTreeUtils.java
@@ -0,0 +1,53 @@
+/*
+ * 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.iotdb.commons.path;
+
+public class PathPatternTreeUtils {
+ /**
+ * Intersect the pattern tree with the full path prefix tree, and return the
intersected pattern
+ * tree.
+ *
+ * @param patternTree any pattern tree
+ * @param fullPathPrefixTree the included pattern must be fullPath or prefix
pattern(e.g.
+ * root.sg.**)
+ * @return the intersected pattern tree
+ */
+ public static PathPatternTree intersectWithFullPathPrefixTree(
+ PathPatternTree patternTree, PathPatternTree fullPathPrefixTree) {
+ PathPatternTree result = new PathPatternTree();
+ for (PartialPath pathPattern : patternTree.getAllPathPatterns()) {
+ for (PartialPath fullPathOrPrefix :
fullPathPrefixTree.getAllPathPatterns()) {
+ if (fullPathOrPrefix.endWithMultiLevelWildcard()) {
+ // prefix
+ for (PartialPath temp :
pathPattern.intersectWithPrefixPattern(fullPathOrPrefix)) {
+ result.appendPathPattern(temp);
+ }
+ } else {
+ // full path
+ if (pathPattern.matchFullPath(fullPathOrPrefix)) {
+ result.appendPathPattern(fullPathOrPrefix);
+ }
+ }
+ }
+ }
+ result.constructTree();
+ return result;
+ }
+}
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/path/PathPatternUtil.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/path/PathPatternUtil.java
index 6d5b265a910..a8ba920813b 100644
---
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/path/PathPatternUtil.java
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/path/PathPatternUtil.java
@@ -36,8 +36,8 @@ public class PathPatternUtil {
return node.startsWith(ONE_LEVEL_PATH_WILDCARD) ||
node.endsWith(ONE_LEVEL_PATH_WILDCARD);
}
- public static boolean hasMultiLevelMatchWildcard(String node) {
- return node.startsWith(ONE_LEVEL_PATH_WILDCARD);
+ public static boolean isMultiLevelMatchWildcard(String node) {
+ return MULTI_LEVEL_PATH_WILDCARD.equals(node);
}
/**
diff --git
a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/path/PartialPathTest.java
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/path/PartialPathTest.java
index 8874c34fd4e..c9917ccb8c8 100644
---
a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/path/PartialPathTest.java
+++
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/path/PartialPathTest.java
@@ -24,7 +24,10 @@ import org.junit.Assert;
import org.junit.Test;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import static org.junit.Assert.fail;
@@ -682,6 +685,71 @@ public class PartialPathTest {
}
}
+ @Test
+ public void testIntersectWithPrefixPattern() throws Exception {
+ checkIntersect(
+ new PartialPath("root.**.d*"),
+ new PartialPath("root.test.dac.device1.**"),
+ new HashSet<PartialPath>() {
+ {
+ add(new PartialPath("root.test.dac.device1.d*"));
+ add(new PartialPath("root.test.dac.device1.**.d*"));
+ }
+ });
+ checkIntersect(
+ new PartialPath("root.**.d*.**"),
+ new PartialPath("root.test.dac.device1.**"),
+ new HashSet<PartialPath>() {
+ {
+ add(new PartialPath("root.test.dac.device1.**"));
+ }
+ });
+ checkIntersect(
+ new PartialPath("root.**.d1.**"),
+ new PartialPath("root.sg1.d1.**"),
+ new HashSet<PartialPath>() {
+ {
+ add(new PartialPath("root.sg1.d1.**"));
+ }
+ });
+ checkIntersect(
+ new PartialPath("root.**.d1.s1"),
+ new PartialPath("root.sg1.d1.**"),
+ new HashSet<PartialPath>() {
+ {
+ add(new PartialPath("root.sg1.d1.s1"));
+ add(new PartialPath("root.sg1.d1.d1.s1"));
+ add(new PartialPath("root.sg1.d1.**.d1.s1"));
+ }
+ });
+ checkIntersect(
+ new PartialPath("root.**.d*"),
+ new PartialPath("root.sg1.d1.**"),
+ new HashSet<PartialPath>() {
+ {
+ add(new PartialPath("root.sg1.d1.d*"));
+ add(new PartialPath("root.sg1.d1.**.d*"));
+ }
+ });
+ checkIntersect(
+ new PartialPath("root.sg1.d1"), new PartialPath("root.sg1.d1.**"),
Collections.emptySet());
+ checkIntersect(
+ new PartialPath("root.sg1.d2.s1"),
+ new PartialPath("root.sg1.d1.**"),
+ Collections.emptySet());
+ }
+
+ private void checkIntersect(PartialPath pattern, PartialPath prefix,
Set<PartialPath> expected) {
+ List<PartialPath> actual = pattern.intersectWithPrefixPattern(prefix);
+ for (PartialPath path : actual) {
+ if (!expected.contains(path)) {
+ System.out.println(path);
+ }
+ Assert.assertTrue(expected.remove(path));
+ }
+ Assert.assertTrue(expected.isEmpty());
+ }
+
private void checkNodes(String[] expected, String[] actual) {
Assert.assertEquals(expected.length, actual.length);
for (int i = 0; i < expected.length; i++) {
diff --git
a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/path/PathPatternTreeTest.java
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/path/PathPatternTreeTest.java
index f8b3dea5acb..1566a82f75a 100644
---
a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/path/PathPatternTreeTest.java
+++
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/path/PathPatternTreeTest.java
@@ -29,7 +29,9 @@ import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.stream.Collectors;
public class PathPatternTreeTest {
@@ -287,4 +289,123 @@ public class PathPatternTreeTest {
Arrays.asList(new PartialPath("root.sg1.*.t1.s1"), new
PartialPath("root.sg1.d1.t2.s2")),
patternTree.getAllPathPatterns());
}
+
+ @Test
+ public void testIntersectWithFullPathPrefixTree1() throws Exception {
+ List<PartialPath> partialPathList =
+ Arrays.asList(
+ new PartialPath("root.sg1.d1.t1.s1"),
+ new PartialPath("root.sg1.*.t1.s1"),
+ new PartialPath("root.sg1.d2.**"));
+ PathPatternTree patternTree = new PathPatternTree();
+ for (PartialPath path : partialPathList) {
+ patternTree.appendPathPattern(path);
+ }
+ patternTree.constructTree();
+
+ List<PartialPath> fullPathPrefixList =
+ Arrays.asList(
+ new PartialPath("root.sg1.d1.**"),
+ new PartialPath("root.sg1.d2.**"),
+ new PartialPath("root.sg1.d3.t1.s1"));
+ PathPatternTree fullPathPrefixTree = new PathPatternTree();
+ for (PartialPath path : fullPathPrefixList) {
+ fullPathPrefixTree.appendPathPattern(path);
+ }
+ fullPathPrefixTree.constructTree();
+
+ PathPatternTree res =
+ PathPatternTreeUtils.intersectWithFullPathPrefixTree(patternTree,
fullPathPrefixTree);
+ Set<PartialPath> expected =
+ new HashSet<PartialPath>() {
+ {
+ add(new PartialPath("root.sg1.d1.t1.s1"));
+ add(new PartialPath("root.sg1.d2.**"));
+ add(new PartialPath("root.sg1.d3.t1.s1"));
+ }
+ };
+ for (PartialPath path : res.getAllPathPatterns()) {
+ Assert.assertTrue(expected.remove(path));
+ }
+ Assert.assertTrue(expected.isEmpty());
+ }
+
+ @Test
+ public void testIntersectWithFullPathPrefixTree2() throws Exception {
+ List<PartialPath> partialPathList =
+ Arrays.asList(
+ new PartialPath("root.sg1.d1.t1.s1"),
+ new PartialPath("root.sg1.*.t1.s2"),
+ new PartialPath("root.**.t*.**"));
+ PathPatternTree patternTree = new PathPatternTree();
+ for (PartialPath path : partialPathList) {
+ patternTree.appendPathPattern(path);
+ }
+ patternTree.constructTree();
+
+ List<PartialPath> fullPathPrefixList =
+ Arrays.asList(new PartialPath("root.sg1.d1.**"), new
PartialPath("root.sg1.d2.**"));
+ PathPatternTree fullPathPrefixTree = new PathPatternTree();
+ for (PartialPath path : fullPathPrefixList) {
+ fullPathPrefixTree.appendPathPattern(path);
+ }
+ fullPathPrefixTree.constructTree();
+
+ PathPatternTree res =
+ PathPatternTreeUtils.intersectWithFullPathPrefixTree(patternTree,
fullPathPrefixTree);
+ Set<PartialPath> expected =
+ new HashSet<PartialPath>() {
+ {
+ add(new PartialPath("root.sg1.d1.t*.**"));
+ add(new PartialPath("root.sg1.d1.**.t*.**"));
+ add(new PartialPath("root.sg1.d2.t*.**"));
+ add(new PartialPath("root.sg1.d2.**.t*.**"));
+ }
+ };
+ for (PartialPath path : res.getAllPathPatterns()) {
+ Assert.assertTrue(expected.remove(path));
+ }
+ Assert.assertTrue(expected.isEmpty());
+ }
+
+ @Test
+ public void testIntersectWithFullPathPrefixTree3() throws Exception {
+ List<PartialPath> partialPathList =
+ Arrays.asList(
+ new PartialPath("root.sg1.d1.t1.s1"),
+ new PartialPath("root.sg1.*.t1.s2"),
+ new PartialPath("root.**.h*.**"));
+ PathPatternTree patternTree = new PathPatternTree();
+ for (PartialPath path : partialPathList) {
+ patternTree.appendPathPattern(path);
+ }
+ patternTree.constructTree();
+
+ List<PartialPath> fullPathPrefixList =
+ Arrays.asList(new PartialPath("root.sg1.d1.**"), new
PartialPath("root.sg1.d2.**"));
+ PathPatternTree fullPathPrefixTree = new PathPatternTree();
+ for (PartialPath path : fullPathPrefixList) {
+ fullPathPrefixTree.appendPathPattern(path);
+ }
+ fullPathPrefixTree.constructTree();
+
+ PathPatternTree res =
+ PathPatternTreeUtils.intersectWithFullPathPrefixTree(patternTree,
fullPathPrefixTree);
+ Set<PartialPath> expected =
+ new HashSet<PartialPath>() {
+ {
+ add(new PartialPath("root.sg1.d1.h*.**"));
+ add(new PartialPath("root.sg1.d1.t1.s1"));
+ add(new PartialPath("root.sg1.d1.t1.s2"));
+ add(new PartialPath("root.sg1.d1.**.h*.**"));
+ add(new PartialPath("root.sg1.d2.h*.**"));
+ add(new PartialPath("root.sg1.d2.**.h*.**"));
+ add(new PartialPath("root.sg1.d2.t1.s2"));
+ }
+ };
+ for (PartialPath path : res.getAllPathPatterns()) {
+ Assert.assertTrue(expected.remove(path));
+ }
+ Assert.assertTrue(expected.isEmpty());
+ }
}