Steven Lange

Filters, Queries, and the StarTeam SDK

 

Overview

If you’ve ever used a StarTeam client, you’ve used a filter and/or a query when you look at items within a StarTeam folder.  Filters and queries are great tools for organizing and managing the data that StarTeam presents to you.  You can create a virtual "inbox", showing only items for which you are responsible, only open items, items sorted by severity or priority, or files with a given set of extensions.  Many times when you look at data in StarTeam you’re looking for something - filters and queries will help you find it more easily.

With the StarTeam SDK, the same convenience applies.  When using the ItemListManager class, you can apply filters and queries just as you do within the client.  However, the API allows you to create filters and queries "in memory" for the purpose of criteria matching dynamically (i.e. the criteria for the desired data set may need to change programmatically).  Of course, a few caveats apply, but the effectiveness of using filters and queries via the SDK becomes quite obvious once put into use.

This article will show you how to create, access, and use both filters and queries with the SDK to better help you find specific StarTeam items.

ItemListManager class

Before jumping completely into filters and queries, it’s important to have a basic understanding of the ItemListManager class.  This class is great for capturing large sets of data in only a few calls to the StarTeam server.  It’s closest UI translation would be the upper-right (items) pane, which displays items based on the selected folder, all descendants, access rights, and any applied filters or queries.  The ItemListManager class is probably the most efficient way to collect data from multiple folders at once, without having to use any recursive functions in your code.

Here is a quick, basic sample of how you can use the ItemListManager class to get all the Change Requests in a given view:

    private Items GetAllCRsInView(View view){
// Create a FolderListManager object
FolderListManager flm = new FolderListManager(view);

// Add all folders in the view to the FolderList Manager object
// (this is the equivalent of using "All Descendants"

flm.includeFolders(view.getRootFolder(),-1);

// Get the Change Request type object. This specifies that we want
// CR’s from the folders in the FolderListManager object.

Type crType = view.getServer().typeForName(view.getTypeNames().CHANGEREQUEST);

// Create the ItemListManager object, specifying the type of items
// we want and the FolderListManager object to use.

ItemListManager ilm = new ItemListManager(crType,flm);

return ilm.getItems();
}


If you look at the API documentation, you’ll see that there are a lot of other options you can use when performing this type of operation.  But the above should serve as a basic, functional example.


For a great write-up on how you can optimize the ItemListManager class, check out this article from CodeAlloy: http://www.codealloy.com/sdkoptimize.htm


Now that that’s taken care of, let’s move on.  The rest of this article will build off of this initial function.


Filters


Definition: A filter is a named arrangement of data that consists of a set of fields (used as column headers), sorting and grouping information, and (usually) a query. Once a filter has been created, it can be used in every project that has the same server configuration. You can filter data several different ways:



  • By applying an existing filter.
  • By arranging the data (changing displayed fields, sorting and grouping the files, etc.) and applying a query. You can then use this arrangement as the basis for a new filter.
  • By creating a new filter from scratch.

In this article we’ll cover the first and third methods.


Applying an Existing Filter


To apply an existing filter to an ItemListManager object, you’ll need to acquire the appropriate Filter object first.  Filters can be retrieved using the Type class in the StarTeam SDK.  Why?  Because filters, by definition, are global to the server (not bound to a given project or view), yet specific to a type of StarTeam item.  The method call for getting a Filter object, assuming you know the name of the filter, is Type.getFilterForName() (please note that there is also a Type.getFilterForID(), which takes the ID of the filter you want).  I also recommend using Type.hasFilterForName() to ensure that you’re specifying the correct filter (in case of a type-o, this is much easier than trying to trap an exception because StarTeam couldn’t find the filter you wanted).


Once you have the correct Filter object, you can call ItemListManager.SetFilter() to apply the filter.


So, lets expand the example function above to include what we’ve just talked about.  This time, we’ll also pass the name of the filter we want to apply as an additional parameter.

    private Items GetAllCRsInView(View view, String filtername){
FolderListManager flm = new FolderListManager(view);
flm.includeFolders(view.getRootFolder(),-1);
Type crType = view.getServer().typeForName(view.getTypeNames().CHANGEREQUEST);

ItemListManager ilm = new ItemListManager(crType,flm);

// Check to see if the named filter exists
if (crType.hasFilterForName(filtername)){
// It does exist, so let’s get it
Filter filter = crType.getFilterForName(filtername);
// Now, apply it to the ItemListManager object
ilm.setFilter(filter);
}

return ilm.getItems();
}


Creating a New Filter from Scratch


To Create a new filter, you construct a new Filter object, specify what fields you wish to include (this tells StarTeam what properties to send back from the Server in a single response), and any sorting or grouping options you want.


In the below example, we’ll create an "in memory" filter, that is, one that is not saved to the server as a permanent filter.  If you wish to create a permanent filter, you simply need to call Filter.update() prior to the function’s return statement.  By creating an "in memory" filter, you can dynamically customize how your program will retrieve data from StarTeam, without necessarily affecting the server’s filter list (and other users’ filter list, for that matter).  This allows you to take advantage of filtering multiple times in your program without pushing extra filter data to the StarTeam database.


The function below creates an "in memory" filter for Change Request items, capturing the CR number, status, and synopsis properties, and sorts them by status.

    private Filter CreateNewFilter(Server server){
// We need the Type object for Change Request items
Type crType = server.typeForName(server.getTypeNames().CHANGEREQUEST);
// Create the new filter
Filter filter = new Filter(crType,"My New Filter",Filter.CONTEXT_SERVER,false);

// Add the columns (properties) that we want to include
// Add the CR Number property

filter.appendColumn(server.getPropertyNames().CR_CHANGE_NUMBER, 1);
// Add the Status property
filter.appendColumn(server.getPropertyNames().CR_STATUS, 1);
// Add the Synopsis property
filter.appendColumn(server.getPropertyNames().CR_SYNOPSIS, 1);

// Specify that we want to sort by Status
filter.appendGroupSortInfo(false,server.getPropertyNames().CR_STATUS,true);

// If we wanted to make this filter a permanent one (saved to the
// StarTeam Server, we would uncomment the next line:
//filter.update();

return filter;
}


So to use this new function with our base example, we would change the initial function to use the temporary filter created by the above function:

    private Items GetAllCRsInView(View view){
FolderListManager flm = new FolderListManager(view);
flm.includeFolders(view.getRootFolder(),-1);
Type crType = view.getServer().typeForName(view.getTypeNames().CHANGEREQUEST);

ItemListManager ilm = new ItemListManager(crType,flm);

// Get the new temporary filter
Filter filter = CreateNewFilter(view.getServer());
ilm.setFilter(filter);

return ilm.getItems();
}


Now we’ll move on to queries.  At the end of the next section, you’ll see how you can use both a filter and a query with the ItemListManager object.



Queries


Definition:  A query is a feature that you can use a query to limit the items displayed in the upper pane. Each query is performed on all items in the StarTeam folder and component you have selected. Once a query has been created, it can be used in every project in the same server configuration.


StarTeam queries have the following attributes:



  • A unique name that easily identifies the query. Query names are not case-sensitive.
  • Public or private status. Public queries can be used by anyone with the appropriate access rights, while private queries are available only to your user ID. Once a query has been saved with a specific status, its status cannot be changed. However, you can copy a query and change the state of the new query.
  • A logical expression appropriate for items of a particular type. These expressions include one or more conditions. A condition consists of a field (not necessarily a current column header), a relational operator, and a value to be compared to the value of the field. For example, a condition used to locate change requests might be: Responsibility Equals Rhonda Thurman.

With the SDK, queries can be used with the ItemListManager class a few ways:



  • Applying a query to a populated ItemListManager object to get a subset of results (a "populated" ItemListManager object is one that has already retrieved data from the server by calling methods like .getItems() and .getItemsArray()).
  • Applying an existing query to a Filter, which is then in turn applied to an ItemListManager object.
  • Creating a new query, and utilizing it with one of the first two methods above.

Applying a Query to a Populated ItemListManager Object


To apply an existing query to an ItemListManager object, you’ll need to acquire the appropriate QueryInfo object first.  QueryInfo objects can be retrieved using the Type class in the StarTeam SDK for the same reason described above for filters.  The method call for getting a QueryInfo object, assuming you know the name of the query, is Type.getQueryForName() (please note that, like filters, there is also a Type.getQueryForID(), which takes the ID of the query you want).  I again recommend using Type.hasQueryForName() to ensure that you’re referring to the correct query.


Once you have the correct QueryInfo object, you can call ItemListManager.selectByQuery() to apply the query and get a subset of results matching the query’s criteria.


Now let’s see a basic example of this, again building from the base example:

    private Items GetAllCRsInView(View view, String queryname){
FolderListManager flm = new FolderListManager(view);
flm.includeFolders(view.getRootFolder(),-1);
Type crType = view.getServer().typeForName(view.getTypeNames().CHANGEREQUEST);

ItemListManager ilm = new ItemListManager(crType,flm);

// Check to see if the named query exists
if (crType.hasQueryForName(queryname)){
// It does exist, so let’s get it
QueryInfo query = crType.getQueryForName(queryname);

// "Populate" the ItemListManager object
ilm.getItems();
// Now, apply the query and return the subset
return ilm.selectByQuery(query);
}
else {
return ilm.getItems();
}
}


Note that ilm.getItems() is called prior to ilm.selectByQuery().  This is necessary for the second call to have an initial result set to which it can apply the query.  If you don’t "populate" the ItemListManager object first, you will receive no results.


Applying an Existing Query to a Filter


If you’ve ever looked at the Filter dialog in the StarTeam client, you’ve probably see that a filter can have a query associated with it.  This is convenient in that you can specify both what items to display (query) and how they are displayed (filter) in a single operation.


With the SDK, applying a query to a filter is effectively the same thing.  You can apply an existing (not "in memory") QueryInfo object to a Filter object and save your changes (so the filter permanently uses the specified query), or programmatically use the filter/query combination with an ItemListManager object and dispose of it when done.


To show this, let’s expand on the "CreateNewFilter()" function above, and apply a named query as well.

    private Filter CreateNewFilter(Server server, String queryname){
Type crType = server.typeForName(server.getTypeNames().CHANGEREQUEST);

Filter filter = new Filter(crType,"My New Filter",Filter.CONTEXT_SERVER,false);

filter.appendColumn(server.getPropertyNames().CR_CHANGE_NUMBER, 1);
filter.appendColumn(server.getPropertyNames().CR_STATUS, 1);
filter.appendColumn(server.getPropertyNames().CR_SYNOPSIS, 1);

filter.appendGroupSortInfo(false,server.getPropertyNames().CR_STATUS,true);

// Look for and apply the named query, if it exists
if (crType.hasQueryForName(queryname)){
// It does exist, so let’s get it
QueryInfo query = crType.getQueryForName(queryname);
// Associate the existing query with the new filter
filter.setQueryID(query.getID());
}

// If we wanted to make this filter a permanent one (saved to the
// StarTeam Server, we would uncomment the next line:
//filter.update();

return filter;
}


Now, if we were to apply this filter to an ItemListManager object as described above, both the filter and the associated query would be applied.  This is more efficient than calling .selectByQuery() because you are not forced to fetch the entire superset of items from the server before applying a query.  It is applied "on the fly" because it is included with the filter (which is applied on the server side during the initial fetch of items).


Here’s the caveat:  To associate a query with a filter, the query must be an existing or permanent filter on the server - it cannot reside solely in memory.  This is because since the filter is applied on the server side, the server needs to know about the associated query to use (which is impossible if the query exists only in client-side memory).  If you try to apply an "in memory" query to a filter (Filter.setQueryID()), you’ll effectively be passing -1 as the query’s ID, which will ultimately be ignored by the Filter and ItemListManager.


Creating a New Query


Creating queries can be very useful for creating multi-conditional searches within your application.  Queries can have multiple logical operators containing multiple logical expressions, basically forming a query tree.  To create a new query, you need to first understand the implications of where you set query "nodes" and their expressions to get the expected result.


The below example is relatively simple, but should provide a good framework to allow you to build more complex, specific queries.  This example will create a query for Change Requests that finds CR’s with a "New" status and whose synopsis contains the word "StarDraw":

    private QueryInfo CreateNewQuery(Server server){
Type crType = server.typeForName(server.getTypeNames().CHANGEREQUEST);

// Create a QueryNode
QueryNode node = new QueryNode(QueryNode.OP_AND);

// Create a QueryPart for: Status = "New"
int propertyID = crType.propertyForName(server.getPropertyNames().CR_STATUS).getID();
int statusNew = server.getPropertyEnums().CR_STATUS_NEW;
QueryPart part = new QueryPart(propertyID,QueryPart.REL_EQUAL,statusNew);
// Add the QueryPart to the QueryNode
node.appendQueryPart(part);

// Create a QueryPart for: Synopsis contains "StarDraw" (ignore case)
propertyID = crType.propertyForName(server.getPropertyNames().CR_SYNOPSIS).getID();
part = new QueryPart(propertyID,QueryPart.REL_TEXTSEARCH,"StarDraw",false);
// Add the QueryPart to the QueryNode
node.appendQueryPart(part);

// Create the QueryInfo object
QueryInfo query = new QueryInfo(crType,false,"My Temporary Query",node);

// If we wanted to make this query a permanent one (saved to the
// StarTeam Server), we would uncomment the next line:
//query.update();

return query;
}


I recommend that you play around with adding additional QueryNode objects at different hierarchies, and different types of QueryPart objects (data types, operators, etc.).



Well, I hope this helps some of you become more familiar with how you can use both filters and queries with the SDK in your applications.  There are several additional layers of detail that you can get into with the ItemListManager, Filter, and Query* classes, and the best way to tap those layers is to take a test drive.


Good luck, and happy programming!

Posted by Steven Lange on June 10th, 2005 under Source Code, StarTeam |



2 Responses to “Filters, Queries, and the StarTeam SDK”

  1. Marc Veilleux Says:

    Thank you so much!



Server Response from: blog2.codegear.com