Skip to content

Commit e7dcdf0

Browse files
committed
improve pruneInclusionPatterns & pruneIrrelevantExclusions
1 parent 6e3cf37 commit e7dcdf0

File tree

1 file changed

+58
-79
lines changed
  • iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/datastructure/pattern

1 file changed

+58
-79
lines changed

iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/datastructure/pattern/TreePattern.java

Lines changed: 58 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,19 @@ private static List<TreePattern> optimizePatterns(final List<TreePattern> patter
417417
/**
418418
* Prunes patterns from the inclusion list that are fully covered by ANY pattern in the exclusion
419419
* list.
420+
*
421+
* <p><b>Optimization Strategy:</b>
422+
*
423+
* <ol>
424+
* <li><b>Build Exclusion Trie:</b> Construct a Trie containing all paths from the exclusion
425+
* list. This aggregates the coverage of all exclusion patterns into a single structure.
426+
* <li><b>Check Coverage:</b> Iterate through the inclusion list. For each inclusion pattern,
427+
* check if ALL of its represented paths are covered by the Exclusion Trie. If so, the
428+
* pattern is redundant and removed.
429+
* </ol>
430+
*
431+
* <p><b>Time Complexity:</b> O((N + M) * L), where N is the number of inclusion patterns, M is
432+
* the number of exclusion patterns, and L is the average path length.
420433
*/
421434
private static List<TreePattern> pruneInclusionPatterns(
422435
final List<TreePattern> inclusion, final List<TreePattern> exclusion) {
@@ -427,15 +440,29 @@ private static List<TreePattern> pruneInclusionPatterns(
427440
return inclusion;
428441
}
429442

443+
// 1. Build Trie with all exclusion paths
444+
// The Trie represents the union of all excluded areas.
445+
final PatternTrie exclusionTrie = new PatternTrie();
446+
for (final TreePattern exc : exclusion) {
447+
for (final PartialPath path : exc.getBaseInclusionPaths()) {
448+
exclusionTrie.add(path);
449+
}
450+
}
451+
430452
final List<TreePattern> prunedInclusion = new ArrayList<>();
453+
// 2. Filter inclusion patterns
431454
for (final TreePattern inc : inclusion) {
432-
boolean isFullyExcluded = false;
433-
for (final TreePattern exc : exclusion) {
434-
if (covers(exc, inc)) {
435-
isFullyExcluded = true;
455+
boolean isFullyExcluded = true;
456+
// An inclusion pattern is fully excluded only if ALL its constituent base paths
457+
// are covered by the exclusion Trie.
458+
for (final PartialPath path : inc.getBaseInclusionPaths()) {
459+
if (!exclusionTrie.isCovered(path)) {
460+
isFullyExcluded = false;
436461
break;
437462
}
438463
}
464+
465+
// If not fully excluded (i.e., at least one path survives), keep it.
439466
if (!isFullyExcluded) {
440467
prunedInclusion.add(inc);
441468
}
@@ -446,105 +473,57 @@ private static List<TreePattern> pruneInclusionPatterns(
446473
/**
447474
* Prunes patterns from the exclusion list that do NOT overlap with any of the remaining inclusion
448475
* patterns.
476+
*
477+
* <p><b>Optimization Strategy:</b>
478+
*
479+
* <ol>
480+
* <li><b>Build Inclusion Trie:</b> Construct a Trie containing all paths from the inclusion
481+
* list. This aggregates the search space of inclusion patterns.
482+
* <li><b>Filter Exclusions:</b> Iterate through the exclusion list. For each exclusion pattern,
483+
* check if it overlaps with the Inclusion Trie. Only exclusions that overlap with at least
484+
* one inclusion pattern are kept.
485+
* </ol>
486+
*
487+
* <p><b>Time Complexity:</b> O((N + M) * L), where N is the number of inclusion patterns, M is
488+
* the number of exclusion patterns, and L is the average path length.
449489
*/
450490
private static List<TreePattern> pruneIrrelevantExclusions(
451491
final List<TreePattern> inclusion, final List<TreePattern> exclusion) {
452492
if (exclusion == null || exclusion.isEmpty()) {
453493
return new ArrayList<>();
454494
}
455495
if (inclusion == null || inclusion.isEmpty()) {
456-
// If inclusion is empty, exclusion is irrelevant anyway, but usually this case
457-
// throws exception earlier.
496+
// If inclusion is empty, exclusion is irrelevant anyway.
458497
return new ArrayList<>();
459498
}
460499

500+
// 1. Build Trie from Inclusion Patterns
501+
final PatternTrie inclusionTrie = new PatternTrie();
502+
for (final TreePattern inc : inclusion) {
503+
for (final PartialPath path : inc.getBaseInclusionPaths()) {
504+
inclusionTrie.add(path);
505+
}
506+
}
507+
508+
// 2. Filter Exclusion Patterns using the Trie
461509
final List<TreePattern> relevantExclusion = new ArrayList<>();
462510
for (final TreePattern exc : exclusion) {
463511
boolean overlapsWithAnyInclusion = false;
464-
for (final TreePattern inc : inclusion) {
465-
if (overlaps(exc, inc)) {
512+
// An exclusion pattern is relevant if ANY of its base paths overlap with the inclusion Trie
513+
for (final PartialPath path : exc.getBaseInclusionPaths()) {
514+
if (inclusionTrie.overlaps(path)) {
466515
overlapsWithAnyInclusion = true;
467516
break;
468517
}
469518
}
519+
470520
if (overlapsWithAnyInclusion) {
471521
relevantExclusion.add(exc);
472522
}
473523
}
474524
return relevantExclusion;
475525
}
476526

477-
/** Checks if 'coverer' pattern fully covers 'coveree' pattern. */
478-
private static boolean covers(final TreePattern coverer, final TreePattern coveree) {
479-
try {
480-
final List<PartialPath> covererPaths = coverer.getBaseInclusionPaths();
481-
final List<PartialPath> covereePaths = coveree.getBaseInclusionPaths();
482-
483-
if (covererPaths.isEmpty() || covereePaths.isEmpty()) {
484-
return false;
485-
}
486-
487-
// Logic: For 'coverer' to cover 'coveree', ALL paths in 'coveree' must be included
488-
// by at least one path in 'coverer'.
489-
for (final PartialPath sub : covereePaths) {
490-
boolean isSubCovered = false;
491-
for (final PartialPath sup : covererPaths) {
492-
if (sup.include(sub)) {
493-
isSubCovered = true;
494-
break;
495-
}
496-
}
497-
if (!isSubCovered) {
498-
return false;
499-
}
500-
}
501-
return true;
502-
} catch (final Exception e) {
503-
// In case of path parsing errors or unsupported operations, assume no coverage
504-
// to be safe and avoid aggressive pruning.
505-
LOGGER.warn(
506-
"Pipe: Failed to check if pattern [{}] covers [{}]. Assuming false.",
507-
coverer.getPattern(),
508-
coveree.getPattern(),
509-
e);
510-
return false;
511-
}
512-
}
513-
514-
/** Checks if 'patternA' overlaps with 'patternB' using a Trie optimization. */
515-
private static boolean overlaps(final TreePattern patternA, final TreePattern patternB) {
516-
try {
517-
final List<PartialPath> pathsA = patternA.getBaseInclusionPaths();
518-
final List<PartialPath> pathsB = patternB.getBaseInclusionPaths();
519-
520-
if (pathsA.isEmpty() || pathsB.isEmpty()) {
521-
return false;
522-
}
523-
524-
// Optimization: Build Trie from the smaller list (usually) or just patternB
525-
// to avoid O(N^2) comparisons.
526-
final PatternTrie trie = new PatternTrie();
527-
for (final PartialPath path : pathsB) {
528-
trie.add(path);
529-
}
530-
531-
// Check if any path in A overlaps with the Trie constructed from B
532-
for (final PartialPath pathA : pathsA) {
533-
if (trie.overlaps(pathA)) {
534-
return true;
535-
}
536-
}
537-
return false;
538-
} catch (final Exception e) {
539-
LOGGER.warn(
540-
"Pipe: Failed to check if pattern [{}] overlaps with [{}]. Assuming false.",
541-
patternA.getPattern(),
542-
patternB.getPattern(),
543-
e);
544-
return false;
545-
}
546-
}
547-
548527
/**
549528
* A private helper method to parse a list of {@link TreePattern}s from the "pattern" parameter,
550529
* considering its "format".

0 commit comments

Comments
 (0)