You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by qi...@apache.org on 2021/12/29 13:19:10 UTC
[iotdb] branch master updated: [IOTDB-2153][IOTDB-2157] fix incorrect path search space (#4581)
This is an automated email from the ASF dual-hosted git repository.
qiaojialin 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 9d9e6b9 [IOTDB-2153][IOTDB-2157] fix incorrect path search space (#4581)
9d9e6b9 is described below
commit 9d9e6b994455baec7eabd49b417afef9dace0dd3
Author: Zhong Wang <wa...@alibaba-inc.com>
AuthorDate: Wed Dec 29 21:18:32 2021 +0800
[IOTDB-2153][IOTDB-2157] fix incorrect path search space (#4581)
---
.../apache/iotdb/cluster/metadata/CMManager.java | 46 +++++----
.../iotdb/cluster/query/ClusterPlanExecutor.java | 26 +++--
.../org/apache/iotdb/db/engine/StorageEngine.java | 29 ++++--
.../org/apache/iotdb/db/metadata/MManager.java | 5 +-
.../org/apache/iotdb/db/metadata/mtree/MTree.java | 7 +-
.../apache/iotdb/db/metadata/path/PartialPath.java | 113 +++++++++++++++++----
.../apache/iotdb/db/metadata/PartialPathTest.java | 55 +++++++---
7 files changed, 209 insertions(+), 72 deletions(-)
diff --git a/cluster/src/main/java/org/apache/iotdb/cluster/metadata/CMManager.java b/cluster/src/main/java/org/apache/iotdb/cluster/metadata/CMManager.java
index d0d6a93..04f5e3d 100644
--- a/cluster/src/main/java/org/apache/iotdb/cluster/metadata/CMManager.java
+++ b/cluster/src/main/java/org/apache/iotdb/cluster/metadata/CMManager.java
@@ -920,7 +920,7 @@ public class CMManager extends MManager {
* @return all paths after removing wildcards in the path
*/
public Set<PartialPath> getMatchedDevices(PartialPath originPath) throws MetadataException {
- Map<String, String> sgPathMap = groupPathByStorageGroup(originPath);
+ Map<String, List<PartialPath>> sgPathMap = groupPathByStorageGroup(originPath);
Set<PartialPath> ret = getMatchedDevices(sgPathMap);
logger.debug("The devices of path {} are {}", originPath, ret);
return ret;
@@ -933,14 +933,14 @@ public class CMManager extends MManager {
* storage group added
* @return a collection of all queried paths
*/
- private List<MeasurementPath> getMatchedPaths(Map<String, String> sgPathMap, boolean withAlias)
- throws MetadataException {
+ private List<MeasurementPath> getMatchedPaths(
+ Map<String, List<PartialPath>> sgPathMap, boolean withAlias) throws MetadataException {
List<MeasurementPath> result = new ArrayList<>();
// split the paths by the data group they belong to
Map<PartitionGroup, List<String>> remoteGroupPathMap = new HashMap<>();
- for (Entry<String, String> sgPathEntry : sgPathMap.entrySet()) {
+ for (Entry<String, List<PartialPath>> sgPathEntry : sgPathMap.entrySet()) {
String storageGroupName = sgPathEntry.getKey();
- PartialPath pathUnderSG = new PartialPath(sgPathEntry.getValue());
+ List<PartialPath> paths = sgPathEntry.getValue();
// find the data group that should hold the timeseries schemas of the storage group
PartitionGroup partitionGroup =
metaGroupMember.getPartitionTable().route(storageGroupName, 0);
@@ -954,7 +954,10 @@ public class CMManager extends MManager {
} catch (CheckConsistencyException e) {
logger.warn("Failed to check consistency.", e);
}
- List<MeasurementPath> allTimeseriesName = getMatchedPathsLocally(pathUnderSG, withAlias);
+ List<MeasurementPath> allTimeseriesName = new ArrayList<>();
+ for (PartialPath path : paths) {
+ allTimeseriesName.addAll(getMatchedPathsLocally(path, withAlias));
+ }
logger.debug(
"{}: get matched paths of {} locally, result {}",
metaGroupMember.getName(),
@@ -963,9 +966,11 @@ public class CMManager extends MManager {
result.addAll(allTimeseriesName);
} else {
// batch the queries of the same group to reduce communication
- remoteGroupPathMap
- .computeIfAbsent(partitionGroup, p -> new ArrayList<>())
- .add(pathUnderSG.getFullPath());
+ for (PartialPath path : paths) {
+ remoteGroupPathMap
+ .computeIfAbsent(partitionGroup, p -> new ArrayList<>())
+ .add(path.getFullPath());
+ }
}
}
@@ -1078,14 +1083,14 @@ public class CMManager extends MManager {
* queried with storage group added
* @return a collection of all queried devices
*/
- private Set<PartialPath> getMatchedDevices(Map<String, String> sgPathMap)
+ private Set<PartialPath> getMatchedDevices(Map<String, List<PartialPath>> sgPathMap)
throws MetadataException {
Set<PartialPath> result = new HashSet<>();
// split the paths by the data group they belong to
Map<PartitionGroup, List<String>> groupPathMap = new HashMap<>();
- for (Entry<String, String> sgPathEntry : sgPathMap.entrySet()) {
+ for (Entry<String, List<PartialPath>> sgPathEntry : sgPathMap.entrySet()) {
String storageGroupName = sgPathEntry.getKey();
- PartialPath pathUnderSG = new PartialPath(sgPathEntry.getValue());
+ List<PartialPath> paths = sgPathEntry.getValue();
// find the data group that should hold the timeseries schemas of the storage group
PartitionGroup partitionGroup =
metaGroupMember.getPartitionTable().route(storageGroupName, 0);
@@ -1099,7 +1104,10 @@ public class CMManager extends MManager {
} catch (CheckConsistencyException e) {
logger.warn("Failed to check consistency.", e);
}
- Set<PartialPath> allDevices = super.getMatchedDevices(pathUnderSG);
+ Set<PartialPath> allDevices = new HashSet<>();
+ for (PartialPath path : paths) {
+ allDevices.addAll(super.getMatchedDevices(path));
+ }
logger.debug(
"{}: get matched paths of {} locally, result {}",
metaGroupMember.getName(),
@@ -1108,9 +1116,11 @@ public class CMManager extends MManager {
result.addAll(allDevices);
} else {
// batch the queries of the same group to reduce communication
- groupPathMap
- .computeIfAbsent(partitionGroup, p -> new ArrayList<>())
- .add(pathUnderSG.getFullPath());
+ for (PartialPath path : paths) {
+ groupPathMap
+ .computeIfAbsent(partitionGroup, p -> new ArrayList<>())
+ .add(path.getFullPath());
+ }
}
}
@@ -1198,7 +1208,7 @@ public class CMManager extends MManager {
@Override
public Pair<List<MeasurementPath>, Integer> getMeasurementPathsWithAlias(
PartialPath pathPattern, int limit, int offset) throws MetadataException {
- Map<String, String> sgPathMap = groupPathByStorageGroup(pathPattern);
+ Map<String, List<PartialPath>> sgPathMap = groupPathByStorageGroup(pathPattern);
List<MeasurementPath> result = getMatchedPaths(sgPathMap, true);
int skippedOffset = 0;
@@ -1229,7 +1239,7 @@ public class CMManager extends MManager {
* @return all paths after removing wildcards in the path
*/
public List<MeasurementPath> getMatchedPaths(PartialPath originPath) throws MetadataException {
- Map<String, String> sgPathMap = groupPathByStorageGroup(originPath);
+ Map<String, List<PartialPath>> sgPathMap = groupPathByStorageGroup(originPath);
List<MeasurementPath> ret = getMatchedPaths(sgPathMap, false);
logger.debug("The paths of path {} are {}", originPath, ret);
return ret;
diff --git a/cluster/src/main/java/org/apache/iotdb/cluster/query/ClusterPlanExecutor.java b/cluster/src/main/java/org/apache/iotdb/cluster/query/ClusterPlanExecutor.java
index 3485cfd..a1ab986 100644
--- a/cluster/src/main/java/org/apache/iotdb/cluster/query/ClusterPlanExecutor.java
+++ b/cluster/src/main/java/org/apache/iotdb/cluster/query/ClusterPlanExecutor.java
@@ -134,7 +134,7 @@ public class ClusterPlanExecutor extends PlanExecutor {
} catch (CheckConsistencyException e) {
throw new MetadataException(e);
}
- Map<String, String> sgPathMap = IoTDB.metaManager.groupPathByStorageGroup(path);
+ Map<String, List<PartialPath>> sgPathMap = IoTDB.metaManager.groupPathByStorageGroup(path);
if (sgPathMap.isEmpty()) {
throw new PathNotExistException(path.getFullPath());
}
@@ -149,7 +149,7 @@ public class ClusterPlanExecutor extends PlanExecutor {
return ret;
}
- private int getDeviceCount(Map<String, String> sgPathMap, PartialPath queryPath)
+ private int getDeviceCount(Map<String, List<PartialPath>> sgPathMap, PartialPath queryPath)
throws CheckConsistencyException, MetadataException {
AtomicInteger result = new AtomicInteger();
// split the paths by the data group they belong to
@@ -311,7 +311,8 @@ public class ClusterPlanExecutor extends PlanExecutor {
if (!wildcardPath.getMeasurement().equals(IoTDBConstant.MULTI_LEVEL_PATH_WILDCARD)) {
wildcardPath = wildcardPath.concatNode(IoTDBConstant.MULTI_LEVEL_PATH_WILDCARD);
}
- Map<String, String> sgPathMap = IoTDB.metaManager.groupPathByStorageGroup(wildcardPath);
+ Map<String, List<PartialPath>> sgPathMap =
+ IoTDB.metaManager.groupPathByStorageGroup(wildcardPath);
if (sgPathMap.isEmpty()) {
return 0;
}
@@ -370,14 +371,14 @@ public class ClusterPlanExecutor extends PlanExecutor {
* @param level the max depth to match the pattern, -1 means matching the whole pattern
* @return the number of paths that match the pattern at given level
*/
- private int getPathCount(Map<String, String> sgPathMap, int level)
+ private int getPathCount(Map<String, List<PartialPath>> sgPathMap, int level)
throws MetadataException, CheckConsistencyException {
AtomicInteger result = new AtomicInteger();
// split the paths by the data group they belong to
Map<PartitionGroup, List<String>> groupPathMap = new HashMap<>();
- for (Entry<String, String> sgPathEntry : sgPathMap.entrySet()) {
+ for (Entry<String, List<PartialPath>> sgPathEntry : sgPathMap.entrySet()) {
String storageGroupName = sgPathEntry.getKey();
- PartialPath pathUnderSG = new PartialPath(sgPathEntry.getValue());
+ List<PartialPath> paths = sgPathEntry.getValue();
// find the data group that should hold the timeseries schemas of the storage group
PartitionGroup partitionGroup =
metaGroupMember.getPartitionTable().route(storageGroupName, 0);
@@ -387,7 +388,10 @@ public class ClusterPlanExecutor extends PlanExecutor {
metaGroupMember
.getLocalDataMember(partitionGroup.getHeader(), partitionGroup.getRaftId())
.syncLeaderWithConsistencyCheck(false);
- int localResult = getLocalPathCount(pathUnderSG, level);
+ int localResult = 0;
+ for (PartialPath path : paths) {
+ localResult += getLocalPathCount(path, level);
+ }
logger.debug(
"{}: get path count of {} locally, result {}",
metaGroupMember.getName(),
@@ -396,9 +400,11 @@ public class ClusterPlanExecutor extends PlanExecutor {
result.addAndGet(localResult);
} else {
// batch the queries of the same group to reduce communication
- groupPathMap
- .computeIfAbsent(partitionGroup, p -> new ArrayList<>())
- .add(pathUnderSG.getFullPath());
+ for (PartialPath path : paths) {
+ groupPathMap
+ .computeIfAbsent(partitionGroup, p -> new ArrayList<>())
+ .add(path.getFullPath());
+ }
}
}
if (groupPathMap.isEmpty()) {
diff --git a/server/src/main/java/org/apache/iotdb/db/engine/StorageEngine.java b/server/src/main/java/org/apache/iotdb/db/engine/StorageEngine.java
index f208490..744cc2d 100644
--- a/server/src/main/java/org/apache/iotdb/db/engine/StorageEngine.java
+++ b/server/src/main/java/org/apache/iotdb/db/engine/StorageEngine.java
@@ -33,7 +33,14 @@ import org.apache.iotdb.db.engine.storagegroup.TsFileResource;
import org.apache.iotdb.db.engine.storagegroup.VirtualStorageGroupProcessor;
import org.apache.iotdb.db.engine.storagegroup.VirtualStorageGroupProcessor.TimePartitionFilter;
import org.apache.iotdb.db.engine.storagegroup.virtualSg.StorageGroupManager;
-import org.apache.iotdb.db.exception.*;
+import org.apache.iotdb.db.exception.BatchProcessException;
+import org.apache.iotdb.db.exception.LoadFileException;
+import org.apache.iotdb.db.exception.ShutdownException;
+import org.apache.iotdb.db.exception.StorageEngineException;
+import org.apache.iotdb.db.exception.StorageGroupProcessorException;
+import org.apache.iotdb.db.exception.TsFileProcessorException;
+import org.apache.iotdb.db.exception.WriteProcessException;
+import org.apache.iotdb.db.exception.WriteProcessRejectException;
import org.apache.iotdb.db.exception.metadata.IllegalPathException;
import org.apache.iotdb.db.exception.metadata.MetadataException;
import org.apache.iotdb.db.exception.metadata.StorageGroupNotSetException;
@@ -722,10 +729,12 @@ public class StorageEngine implements IService {
continue;
}
- PartialPath newPath = path.alterPrefixPath(storageGroupPath);
- processorMap
- .get(storageGroupPath)
- .delete(newPath, startTime, endTime, planIndex, timePartitionFilter);
+ List<PartialPath> possiblePaths = path.alterPrefixPath(storageGroupPath);
+ for (PartialPath possiblePath : possiblePaths) {
+ processorMap
+ .get(storageGroupPath)
+ .delete(possiblePath, startTime, endTime, planIndex, timePartitionFilter);
+ }
}
} catch (IOException | MetadataException e) {
throw new StorageEngineException(e.getMessage());
@@ -744,10 +753,12 @@ public class StorageEngine implements IService {
continue;
}
- PartialPath newPath = path.alterPrefixPath(storageGroupPath);
- processorMap
- .get(storageGroupPath)
- .delete(newPath, Long.MIN_VALUE, Long.MAX_VALUE, planIndex, timePartitionFilter);
+ List<PartialPath> possiblePaths = path.alterPrefixPath(storageGroupPath);
+ for (PartialPath possiblePath : possiblePaths) {
+ processorMap
+ .get(storageGroupPath)
+ .delete(possiblePath, Long.MIN_VALUE, Long.MAX_VALUE, planIndex, timePartitionFilter);
+ }
}
} catch (IOException | MetadataException e) {
throw new StorageEngineException(e.getMessage());
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/MManager.java b/server/src/main/java/org/apache/iotdb/db/metadata/MManager.java
index 2bf02f4..16acda2 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/MManager.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/MManager.java
@@ -1642,8 +1642,9 @@ public class MManager {
* @return StorageGroupName-FullPath pairs
* @apiNote :for cluster
*/
- public Map<String, String> groupPathByStorageGroup(PartialPath path) throws MetadataException {
- Map<String, String> sgPathMap = mtree.groupPathByStorageGroup(path);
+ public Map<String, List<PartialPath>> groupPathByStorageGroup(PartialPath path)
+ throws MetadataException {
+ Map<String, List<PartialPath>> sgPathMap = mtree.groupPathByStorageGroup(path);
if (logger.isDebugEnabled()) {
logger.debug("The storage groups of path {} are {}", path, sgPathMap.keySet());
}
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/MTree.java b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/MTree.java
index 13e43da..d9f6734 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/mtree/MTree.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/mtree/MTree.java
@@ -868,14 +868,15 @@ public class MTree implements Serializable {
* storage group using the children of a mNode. If one child is a storage group node, put a
* storageGroupName-fullPath pair into paths.
*/
- public Map<String, String> groupPathByStorageGroup(PartialPath path) throws MetadataException {
- Map<String, String> result = new HashMap<>();
+ public Map<String, List<PartialPath>> groupPathByStorageGroup(PartialPath path)
+ throws MetadataException {
+ Map<String, List<PartialPath>> result = new HashMap<>();
StorageGroupCollector<Map<String, String>> collector =
new StorageGroupCollector<Map<String, String>>(root, path) {
@Override
protected void collectStorageGroup(IStorageGroupMNode node) {
PartialPath sgPath = node.getPartialPath();
- result.put(sgPath.getFullPath(), path.alterPrefixPath(sgPath).getFullPath());
+ result.put(sgPath.getFullPath(), path.alterPrefixPath(sgPath));
}
};
collector.setCollectInternal(true);
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/path/PartialPath.java b/server/src/main/java/org/apache/iotdb/db/metadata/path/PartialPath.java
index 405f214..6dbee2f 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/path/PartialPath.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/path/PartialPath.java
@@ -41,6 +41,7 @@ import org.apache.iotdb.tsfile.utils.Pair;
import org.apache.iotdb.tsfile.write.schema.IMeasurementSchema;
import org.apache.iotdb.tsfile.write.writer.RestorableTsFileIOWriter;
+import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -51,6 +52,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
+import java.util.stream.Collectors;
import static org.apache.iotdb.db.conf.IoTDBConstant.MULTI_LEVEL_PATH_WILDCARD;
import static org.apache.iotdb.db.conf.IoTDBConstant.ONE_LEVEL_PATH_WILDCARD;
@@ -145,27 +147,104 @@ public class PartialPath extends Path implements Comparable<Path>, Cloneable {
}
/**
- * Construct a new PartialPath by resetting the prefix nodes with prefixPath. If the prefix nodes
- * contains **, then the prefixPath will be set before **. For example, give this = root.**.d and
- * prefixPath = root.a.sg, then the result will be root.a.sg.**.d
+ * Return the intersection of paths starting with the given prefix and paths matching this path
+ * pattern.
*
- * @param prefixPath the prefix path used to replace current nodes
- * @return A new PartialPath with altered prefix
+ * <p>For example, minimizing "root.**.b.c" with prefix "root.a.b" produces "root.a.b.c",
+ * "root.a.b.b.c", "root.a.b.**.b.c", since the multi-level wildcard can match 'a', 'a.b', and any
+ * other sub paths start with 'a.b'.
+ *
+ * <p>The goal of this method is to reduce the search space when querying a storage group with a
+ * path with wildcard.
+ *
+ * @param prefix The prefix. Cannot be null and cannot contain any wildcard.
*/
- public PartialPath alterPrefixPath(PartialPath prefixPath) {
- int newLength = Math.max(nodes.length, prefixPath.getNodeLength());
- int startIndex = Math.min(nodes.length, prefixPath.getNodeLength());
- for (int i = 0; i < startIndex; i++) {
- if (nodes[i].equals(MULTI_LEVEL_PATH_WILDCARD)) {
- newLength += startIndex - i;
- startIndex = i;
- break;
+ public List<PartialPath> alterPrefixPath(PartialPath prefix) {
+ Validate.notNull(prefix);
+
+ // Make sure the prefix path doesn't contain any wildcard.
+ for (String node : prefix.getNodes()) {
+ if (MULTI_LEVEL_PATH_WILDCARD.equals(node) || ONE_LEVEL_PATH_WILDCARD.equals(node)) {
+ throw new IllegalArgumentException(
+ "Wildcards are not allowed in the prefix path: " + prefix.getFullPath());
}
}
- String[] newNodes = Arrays.copyOf(prefixPath.getNodes(), newLength);
- System.arraycopy(
- nodes, startIndex, newNodes, prefixPath.getNodeLength(), nodes.length - startIndex);
- return new PartialPath(newNodes);
+
+ List<List<String>> results = new ArrayList<>();
+ alterPrefixPathInternal(
+ Arrays.asList(prefix.getNodes()), Arrays.asList(nodes), new ArrayList<>(), results);
+ return results.stream()
+ .map(r -> new PartialPath(r.toArray(new String[0])))
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Let PRE stands for the remaining prefix nodes, PATH for the remaining path nodes, and C for the
+ * path being processed. And assume the size of the prefix is i and that of this path is j.
+ *
+ * <p>If the first node of the path is not '**' and it matches the first node of the path. The
+ * optimality equation is,
+ *
+ * <p>O(PRE(0, i), PATH(0, j), C) = O(PRE(1, i), PATH(1, j), [PRE(0)])
+ *
+ * <p>If the first node of the path is '**', it may match the first 1, 2, ... i nodes of the
+ * prefix. '**' may match the all the remaining nodes of the prefix, as well as some additional
+ * levels. Thus, the optimality equation is,
+ *
+ * <p>O(PRE(0, i), PATH(0, j), C) = { O(PRE(1, i), PATH(1, j), [PRE(0)]), ... , O([], PATH(1, j),
+ * C + PRE(0) + ... + PRE(i-1)), O([], PATH(0, j), C + PRE(0) + ... + PRE(i-1))}
+ *
+ * <p>And when all the prefix nodes are matched,
+ *
+ * <p>O([], PATH, C) = C + PATH
+ *
+ * <p>The algorithm above takes all the possible matches of a multi-level wildcard into account.
+ * Thus, it produces the correct result.
+ *
+ * <p>To prevent overlapping among the final results, this algorithm stops when the prefix has no
+ * remaining node and the first remaining node of the path is '**'. This strategy works because
+ * this algorithm will always find the path with the least levels in the search space.
+ *
+ * @param prefix The remaining nodes of the prefix.
+ * @param path The remaining nodes of the path.
+ * @param current The path being processed.
+ * @param results The final results.
+ * @return True if the search should be stopped, else false.
+ */
+ private boolean alterPrefixPathInternal(
+ List<String> prefix, List<String> path, List<String> current, List<List<String>> results) {
+ if (prefix.isEmpty()) {
+ current.addAll(path);
+ results.add(current);
+ return !path.isEmpty() && MULTI_LEVEL_PATH_WILDCARD.equals(path.get(0));
+ }
+ if (path.isEmpty()) {
+ return false;
+ }
+
+ if (MULTI_LEVEL_PATH_WILDCARD.equals(path.get(0))) {
+ // Wildcard matches part of or the whole the prefix.
+ for (int j = 1; j <= prefix.size(); j++) {
+ List<String> copy = new ArrayList<>(current);
+ copy.addAll(prefix.subList(0, j));
+ if (alterPrefixPathInternal(
+ prefix.subList(j, prefix.size()), path.subList(1, path.size()), copy, results)) {
+ return true;
+ }
+ }
+ // Wildcard matches the prefix and some more levels.
+ List<String> copy = new ArrayList<>(current);
+ copy.addAll(prefix);
+ return alterPrefixPathInternal(
+ Collections.emptyList(), path.subList(0, path.size()), copy, results);
+ } else if (ONE_LEVEL_PATH_WILDCARD.equals(path.get(0)) || prefix.get(0).equals(path.get(0))) {
+ // No need to make a copy.
+ current.add(prefix.get(0));
+ return alterPrefixPathInternal(
+ prefix.subList(1, prefix.size()), path.subList(1, path.size()), current, results);
+ }
+
+ return false;
}
/**
diff --git a/server/src/test/java/org/apache/iotdb/db/metadata/PartialPathTest.java b/server/src/test/java/org/apache/iotdb/db/metadata/PartialPathTest.java
index c1cc619..fcde71c 100644
--- a/server/src/test/java/org/apache/iotdb/db/metadata/PartialPathTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/metadata/PartialPathTest.java
@@ -82,19 +82,48 @@ public class PartialPathTest {
@Test
public void testAlterPrefixPath() throws IllegalPathException {
- PartialPath p1 = new PartialPath("root.sg1.d1.s1");
- PartialPath p2 = p1.alterPrefixPath(new PartialPath("root.sg2"));
- PartialPath p3 = p1.alterPrefixPath(new PartialPath("root.sg2.d1.d2.s3"));
-
- Assert.assertEquals("root.sg2.d1.s1", p2.getFullPath());
- Assert.assertEquals("root.sg2.d1.d2.s3", p3.getFullPath());
-
- PartialPath p4 = new PartialPath("root.**.d.s");
- PartialPath p5 = p4.alterPrefixPath(new PartialPath("root.sg"));
- Assert.assertEquals("root.sg.**.d.s", p5.getFullPath());
- PartialPath p6 = new PartialPath("root.a.**.d.s");
- PartialPath p7 = p5.alterPrefixPath(new PartialPath("root.b.sg"));
- Assert.assertEquals("root.b.sg.**.d.s", p7.getFullPath());
+ // Plain path.
+ PartialPath p = new PartialPath("root.a.b.c");
+ List<PartialPath> results = p.alterPrefixPath(new PartialPath("root.a.b"));
+ Assert.assertEquals(results.toString(), 1, results.size());
+ Assert.assertEquals("root.a.b.c", results.get(0).getFullPath());
+
+ // Path with single level wildcard.
+ p = new PartialPath("root.*.b.c");
+ results = p.alterPrefixPath(new PartialPath("root.a.b"));
+ Assert.assertEquals(results.toString(), 1, results.size());
+ Assert.assertEquals("root.a.b.c", results.get(0).getFullPath());
+
+ // Path with multi level wildcard.
+ p = new PartialPath("root.**.b.c");
+ results = p.alterPrefixPath(new PartialPath("root.a.b"));
+ Assert.assertEquals(results.toString(), 3, results.size());
+ Assert.assertTrue(results.toString(), results.contains(new PartialPath("root.a.b.c")));
+ Assert.assertTrue(results.toString(), results.contains(new PartialPath("root.a.b.b.c")));
+ Assert.assertTrue(results.toString(), results.contains(new PartialPath("root.a.b.**.b.c")));
+
+ p = new PartialPath("root.**");
+ results = p.alterPrefixPath(new PartialPath("root.a.b"));
+ Assert.assertEquals(results.toString(), 2, results.size());
+ Assert.assertTrue(results.toString(), results.contains(new PartialPath("root.a.b")));
+ Assert.assertTrue(results.toString(), results.contains(new PartialPath("root.a.b.**")));
+
+ p = new PartialPath("root.**.b.**");
+ results = p.alterPrefixPath(new PartialPath("root.a.b.c"));
+ Assert.assertEquals(results.toString(), 2, results.size());
+ Assert.assertTrue(results.toString(), results.contains(new PartialPath("root.a.b.c")));
+ Assert.assertTrue(results.toString(), results.contains(new PartialPath("root.a.b.c.**")));
+
+ p = new PartialPath("root.**.b.**.b");
+ results = p.alterPrefixPath(new PartialPath("root.b.b.b"));
+ Assert.assertEquals(results.toString(), 2, results.size());
+ Assert.assertTrue(results.toString(), results.contains(new PartialPath("root.b.b.b.b")));
+ Assert.assertTrue(results.toString(), results.contains(new PartialPath("root.b.b.b.**.b")));
+
+ // Path cannot be altered.
+ p = new PartialPath("root.b.c.**");
+ results = p.alterPrefixPath(new PartialPath("root.a.b.c"));
+ Assert.assertEquals(results.toString(), 0, results.size());
}
@Test