New Criteria

Well, we're back from MMS which was pretty successful for Service Manager in general. In a lot of ways, it was a coming out party of sorts as we were able to demonstrate a ton of functionality about the product and really show how well it is coming along. My session with Travis went great and I really feel we did a solid job in speaking to and demonstrating the power of the Service Manager platform. The team is heads down in Beta 2 right now as we push for an early Fall release. Beta 2 is very exciting for us because at that point the product is basically complete from a feature perspective and customers will be able to play with as close to the final bits as they've ever had. I really hope we get a lot more people interested in customization and extension at that time and I'll be excited to blog about a lot of the new features we're introducing in Beta 2 from an SDK perspective.

In this post, I wanted to talk about our new criteria a little bit and introduce you to the new format and some of the new concepts. If you are familiar with OM, the criteria there was very similar to a WHERE clause in SQL. While this was very friendly from an end-user perspective, it actually was quite burdensome from a developer perspective, both in generating complicated criteria and for us in parsing it and ensuring its uniqueness. For Service Manager 2010 we decided to prioritize power and ease of programmatic manipulation over readability and the ability to quickly script the criteria. Criteria is used in for querying for a variety of objects in the SDK. Here's I'll use examples mostly around the instance and projection space.

Simple Expression
A simple expression just compares a property to a value. In this example we are comparing PropertyName on ClassName to the value 3.

                <SimpleExpression>
                <ValueExpressionLeft>
                  <Property>$Target/Property[Type='ClassName']/PropertyName$</Property>
                </ValueExpressionLeft>
                <Operator>Equal</Operator>
                <ValueExpressionRight>
                  <Value>3</Value>
                </ValueExpressionRight>
              </SimpleExpression>.csharpcode, .csharpcode pre
{
    font-size: small;
   color: black;
   font-family: consolas, "Courier New", courier, monospace;
   background-color: #ffffff;
  /*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
   background-color: #f4f4f4;
  width: 100%;
    margin: 0em;
}
.csharpcode .lnum { color: #606060; }

For projections, a simple expression would look like this, where the property is actually a path:

               <SimpleExpression>
                <ValueExpressionLeft>
                  <Property>$Target/Path[Relationship='RelationshipName']/Property[Type='ClassName']/PropertyName$</Property>
                </ValueExpressionLeft>
                <Operator>Equal</Operator>
                <ValueExpressionRight>
                  <Value>3</Value>
                </ValueExpressionRight>
              </SimpleExpression>

Also, rather than using names, you can use Ids in the criteria:

               <SimpleExpression>
                <ValueExpressionLeft>
                  <Property>$Target/Property[Type='b982d3ec-96e0-4901-b1d9-a06196acf0bc']/b982d3ec-96e0-4901-b1d9-a06196acf0bc$</Property>
                </ValueExpressionLeft>
                <Operator>Equal</Operator>
                <ValueExpressionRight>
                  <Value>3</Value>
                </ValueExpressionRight>
              </SimpleExpression>

or:

             <SimpleExpression>
                <ValueExpressionLeft>
                  <Property>$Target/Path[Relationship='b982d3ec-96e0-4901-b1d9-a06196acf0bc']/
Property[Type='b982d3ec-96e0-4901-b1d9-a06196acf0bc']/b982d3ec-96e0-4901-b1d9-a06196acf0bc$</Property>
                </ValueExpressionLeft>
                <Operator>Equal</Operator>
                <ValueExpressionRight>
                  <Value>3</Value>
                </ValueExpressionRight>
            </SimpleExpression>

Unary/Regex Expression
These are the same as the simple expressions, but instead Unary and RegEx operators are supported. RegExExpression supports ContainsSubstring, MatchesWildcard, MatchesRegularExpression, DoesNotContainSubstring, DoesNotMatchWildcard and DoesNotMatchRegularExpression. UnaryExpression supports IsNull and IsNotNull.

Contains/NotContains/ContainedBy/NotContainedBy Expression
These expressions allow a traversal to be made either down or up containment to either include or exclude results based the existence of a relationship to other instances. The concept here is similar to an EXISTS query in SQL.

           <Contains>
            <!-- A Contains query from a particular node in the projection; this can also just drop path and the implicit path is the seed -->
            <Path>$Target/Path[Relationship='RelationshipName']$</Path>
            <Class>$MPElement[Name='System!System.Computer']$</Class>
            <Expression>
              <SimpleExpression>
                <ValueExpressionLeft>
                  <Property>$Target/Property[Type='System!System.Computer']/FQDN$</Property>
                </ValueExpressionLeft>
                <Operator>Equal</Operator>
                <ValueExpressionRight>
                  <Value>ComputerName</Value>
                </ValueExpressionRight>
              </SimpleExpression>
            </Expression>
          </Contains>

The Path element indicates where within the context of the criteria you want to perform the containment based query. If the path is excluded, it means that you want the containment query to be done on the target of the criteria, namely the projection seed class or the class in the case of regular instance queries.

As a more concrete example, given an Incident projection with System.Incident as the seed and a single related owner System.User, one can do containment based queries on either the incident or the user. If a query like “Give me all incidents that are contained by the ‘Tier 1 Incident Group’”, the path element would be excluded, the Class element would point to the Incident Group type and the expression would be empty. If there were some criteria on that group that needed to be matched, that expression would be used for that. If the query was like “Give me incidents that have a owner that is contained by “Tier 1 Users Group”, the path element would need to be populated pointing to System.User.

It is important to note that the path element indicates where in the context of the criteria you want the containment query to apply, while the class and criteria indicate what you are searching for in that containment space.

And/Or Expression
These expressions allow you to combine other expressions.

In Expression
And finally we support a basic IN clause for use with Guid fields only. The other thing to note here is that I am using a GenericProperty instead of Property element. This allows you to refer to generic properties of instances that aren't modeled. This property also has a Path attribute that allows you to point to a node in a projection for which you want the generic property to apply.

     <Expression>
      <!-- Simple IN Clause (GUID support only) -->
      <In>
        <GenericProperty>Id</GenericProperty>
        <Values>
          <Value>b982d3ec-96e0-4901-b1d9-a06196acf0bc</Value>
          <Value>b982d3ec-96e0-4901-b1d9-a06197acf0bc</Value>
          <Value>b982d3ec-96e0-4901-b1d9-a06198acf0bc</Value>
        </Values>
      </In>
    </Expression>