瀏覽代碼

Merge pull request #13976 from ghalliday/issue24423

HPCC-24423 Add the ability to filter wudetails by attribute name/value

Reviewed-by: Shamser Ahmed
Merged-by: Gavin Halliday <ghalliday@hpccsystems.com>
Gavin Halliday 5 年之前
父節點
當前提交
d406a9c4ab
共有 3 個文件被更改,包括 124 次插入34 次删除
  1. 69 21
      common/workunit/workunit.cpp
  2. 23 0
      common/workunit/workunit.hpp
  3. 32 13
      esp/services/ws_workunits/ws_wudetails.cpp

+ 69 - 21
common/workunit/workunit.cpp

@@ -2282,25 +2282,39 @@ protected:
                     activeScopes.append(scope);
             }
 
-            if (include)
+            if (include && !filter.includeScope(scope))
+                include = false;
+
+            if (include && filter.requiredStats.size())
             {
-                if (!filter.includeScope(scope))
-                    include = false;
-                else if (filter.requiredStats.size())
+                //MORE: This would be cleaner as a member of filter - but it needs access to the stats.
+                for (unsigned iReq=0; iReq < filter.requiredStats.size(); iReq++)
                 {
-                    //MORE: This would be cleaner as a member of filter - but it needs access to the stats.
-                    for (unsigned iReq=0; iReq < filter.requiredStats.size(); iReq++)
+                    const StatisticValueFilter & cur = filter.requiredStats[iReq];
+                    unsigned __int64 value;
+                    if (getStat(cur.queryKind(), value))
                     {
-                        const StatisticValueFilter & cur = filter.requiredStats[iReq];
-                        unsigned __int64 value;
-                        if (getStat(cur.queryKind(), value))
-                        {
-                            if (!cur.matches(value))
-                                include = false;
-                        }
-                        else
+                        if (!cur.matches(value))
                             include = false;
                     }
+                    else
+                        include = false;
+                }
+            }
+
+            if (include && filter.requiredAttrs.size())
+            {
+                //MORE: This would be cleaner as a member of filter - but it needs access to the stats.
+                StringBuffer temp;
+                for (unsigned iReq=0; iReq < filter.requiredAttrs.size(); iReq++)
+                {
+                    const AttributeValueFilter & cur = filter.requiredAttrs[iReq];
+                    const char * value = queryAttribute(cur.queryKind(), temp.clear());
+                    if (!value || !cur.matches(value))
+                    {
+                        include = false;
+                        break;
+                    }
                 }
             }
 
@@ -2648,6 +2662,15 @@ static unsigned readValue(const char * start, const char * type)
     return value;
 }
 
+StringBuffer & AttributeValueFilter::describe(StringBuffer & out) const
+{
+    out.append(queryWuAttributeName(attr));
+    if (value)
+        out.append("=").append(value);
+    return out;
+}
+
+
 /*
 Scope service matching Syntax:  * indicates
 Which items are matched:
@@ -2983,6 +3006,12 @@ WuScopeFilter & WuScopeFilter::addRequiredStat(StatisticKind statKind)
     return *this;
 }
 
+WuScopeFilter & WuScopeFilter::addRequiredAttr(WuAttr attr, const char * value)
+{
+    requiredAttrs.emplace_back(attr, value);
+    return *this;
+}
+
 //process a filter in one of the following forms:
 //  <statistic-name>
 //  <statistic-name> (=|<|<=|>|>=) <value>
@@ -2996,14 +3025,26 @@ void WuScopeFilter::addRequiredStat(const char * filter)
         cur++;
 
     StringBuffer statisticName(cur-stat, stat);
-    StatisticKind statKind = queryStatisticKind(statisticName, StKindNone);
-    if (statKind == StKindNone)
-        throw makeStringExceptionV(0, "Unknown statistic name '%s'", statisticName.str());
-
     //Skip any spaces before a comparison operator.
     while (*cur && isspace(*cur))
         cur++;
 
+    StatisticKind statKind = queryStatisticKind(statisticName, StKindNone);
+    if (statKind == StKindNone)
+    {
+        WuAttr attr = queryWuAttribute(statisticName, WaNone);
+        if (attr == WaNone)
+            throw makeStringExceptionV(0, "Unknown property name '%s'", statisticName.str());
+
+        if (*cur == '=')
+            requiredAttrs.emplace_back(attr, cur+1);
+        else if (*cur == '\0')
+            requiredAttrs.emplace_back(attr, nullptr);
+        else
+            throw makeStringExceptionV(0, "Unknown attribute comparison '%s'", cur);
+        return;
+    }
+
     //Save the operator, and skip over any non digits.
     const char * op = cur;
     switch (*op)
@@ -3132,11 +3173,11 @@ void WuScopeFilter::finishedFilter()
         if (!(properties & PTscope))
         {
             //Global stats and graph stats only contain stats => remove if not interested in them
-            if (!(properties & PTstatistics))
+            if (!(properties & PTstatistics) && (requiredStats.size() == 0))
                 sourceFlags &= ~(SSFsearchGlobalStats|SSFsearchGraphStats);
 
             //graph, workflow only contains attributes and hints => remove if not interested
-            if (!(properties & (PTattributes|PThints)))
+            if (!(properties & (PTattributes|PThints)) && (requiredAttrs.size() == 0))
                 sourceFlags &= ~(SSFsearchGraph|SSFsearchWorkflow);
         }
 
@@ -3280,7 +3321,7 @@ ScopeCompare WuScopeFilter::compareMatchScopes(const char * scope) const
 StringBuffer & WuScopeFilter::describe(StringBuffer & out) const
 {
     scopeFilter.describe(out);
-    if (requiredStats.size())
+    if (requiredStats.size() || requiredAttrs.size())
     {
         out.append(",where[");
         bool first = false;
@@ -3291,6 +3332,13 @@ StringBuffer & WuScopeFilter::describe(StringBuffer & out) const
             stat.describe(out);
             first = false;
         }
+        for (const auto & attr : requiredAttrs)
+        {
+            if (!first)
+                out.append(",");
+            attr.describe(out);
+            first = false;
+        }
         out.append("]");
     }
 

+ 23 - 0
common/workunit/workunit.hpp

@@ -1016,6 +1016,27 @@ enum WuScopeSourceFlags : unsigned
 };
 BITMASK_ENUM(WuScopeSourceFlags);
 
+class WORKUNIT_API AttributeValueFilter
+{
+public:
+    AttributeValueFilter(WuAttr _attr, const char * _value) : attr(_attr), value(_value)
+    {
+    }
+
+    bool matches(const char * curValue) const
+    {
+        return !value || strsame(curValue, value);
+    }
+
+    WuAttr queryKind() const { return attr; }
+    StringBuffer & describe(StringBuffer & out) const;
+
+protected:
+    WuAttr attr;
+    StringAttr value;
+};
+
+
 /* WuScopeFilter syntax:
  * initial match:   scope[<scope-id>] | stype[<scope-type>] | id[<scope-id>] | depth[<value>| <min>,<max>]
  *                  source[global|stats|graph|exception]
@@ -1054,6 +1075,7 @@ public:
 
     WuScopeFilter & addRequiredStat(StatisticKind statKind);
     WuScopeFilter & addRequiredStat(StatisticKind statKind, stat_type lowValue, stat_type highValue);
+    WuScopeFilter & addRequiredAttr(WuAttr attr, const char * value = nullptr);
 
     void finishedFilter(); // Call once filter has been completely set up
     StringBuffer & describe(StringBuffer & out) const; // describe the filter - each option is preceded by a comma
@@ -1077,6 +1099,7 @@ public:
 //The following members control which scopes are matched by the iterator
     ScopeFilter scopeFilter;                            // Filter that must be matched by a scope
     std::vector<StatisticValueFilter> requiredStats;    // The attributes that must be present for a particular scope
+    std::vector<AttributeValueFilter> requiredAttrs;
     WuScopeSourceFlags sourceFlags = SSFsearchDefault;  // Which sources within the workunit should be included.  Default is to calculate from the properties.
 
 // Once a match has been found which scopes are returned?

+ 32 - 13
esp/services/ws_workunits/ws_wudetails.cpp

@@ -583,29 +583,48 @@ void WUDetails::buildPropertyFilter(IArrayOf<IConstWUPropertyFilter> & reqProper
         const char *exactValue = attribFilterItem.getExactValue();
         const char *minValue = attribFilterItem.getMinValue();
         const char *maxValue = attribFilterItem.getMaxValue();
-        const bool hasExactValue = *exactValue!=0;
-        const bool hasMinValue = *minValue!=0;
-        const bool hasMaxValue = *maxValue!=0;
+        const bool hasExactValue = exactValue && *exactValue!=0;
+        const bool hasMinValue = minValue && *minValue!=0;
+        const bool hasMaxValue = maxValue && *maxValue!=0;
 
         if (hasExactValue && (hasMinValue||hasMaxValue))
             throw MakeStringException(ECLWATCH_INVALID_INPUT,
                                       "Invalid Property Filter ('%s') - ExactValue may not be used with MinValue or MaxValue",
                                       propertyName);
+
         const StatisticKind sk = queryStatisticKind(propertyName, StKindNone);
-        if (sk==StKindAll || sk==StKindNone)
+        if (sk==StKindAll)
             throw MakeStringException(ECLWATCH_INVALID_INPUT, "Invalid Property Name ('%s') in Property Filter", propertyName);
-        if (hasExactValue)
+
+        if (sk==StKindNone)
         {
-            stat_type exactVal = atoi64(exactValue);
-            wuScopeFilter.addRequiredStat(sk,exactVal,exactVal);
+            WuAttr attr = queryWuAttribute(propertyName, WaNone);
+            if (attr == WaNone || attr == WaAll)
+                throw MakeStringException(ECLWATCH_INVALID_INPUT, "Invalid Property Name ('%s') in Property Filter", propertyName);
+
+            if (hasMinValue || hasMaxValue)
+                throw MakeStringException(ECLWATCH_INVALID_INPUT, "Range comparisons not supported for attribute '%s' in Property Filter", propertyName);
+
+            if (hasExactValue)
+                wuScopeFilter.addRequiredAttr(attr, exactValue);
+            else
+                wuScopeFilter.addRequiredAttr(attr, nullptr);
         }
-        else if (hasMinValue||hasMaxValue)
+        else
         {
-            stat_type minVal = atoi64(minValue);
-            stat_type maxVal = atoi64(maxValue);
-            wuScopeFilter.addRequiredStat(sk,minVal,maxVal);
+            if (hasExactValue)
+            {
+                stat_type exactVal = atoi64(exactValue);
+                wuScopeFilter.addRequiredStat(sk,exactVal,exactVal);
+            }
+            else if (hasMinValue||hasMaxValue)
+            {
+                stat_type minVal = atoi64(minValue);
+                stat_type maxVal = atoi64(maxValue);
+                wuScopeFilter.addRequiredStat(sk,minVal,maxVal);
+            }
+            else
+                wuScopeFilter.addRequiredStat(sk);
         }
-        else
-            wuScopeFilter.addRequiredStat(sk);
     }
 }