AFSearch Class
- Last UpdatedJan 12, 2026
- 15 minute read
- PI System
- AF SDK 3.2.0
- Developer

Inheritance Hierarchy
Namespace: OSIsoft.AF.Search
Assembly: OSIsoft.AFSDK (in OSIsoft.AFSDK.dll) Version: 3.2.0.7
Syntax
public abstract class AFSearch : IDisposable
Public MustInherit Class AFSearch Implements IDisposable Dim instance As AFSearch
public ref class AFSearch abstract : IDisposable
[<AbstractClassAttribute>] type AFSearch = class interface IDisposable end
The AFSearch type exposes the following members.
Properties
| Name | Description | |
|---|---|---|
| CacheInterval |
The search's cached automatic refresh interval.
| |
| CacheTimeout |
The timeout to clean up the cached search in the server if it has not been used.
| |
| Database |
The AFDatabase to be searched by the query.
| |
| Identity |
This read-only property specifies the identity of the objects returned
from the search.
| |
| PISystem |
The PISystem to be searched by the query.
| |
| SearchName |
The name of the search object.
| |
| ThrowOnError |
Specifies it an exception will be thrown for missing objects or invalid data in the query.
| |
| TokenCollection |
The tokens that represent this search object.
| |
| Tokens | Obsolete.
The tokens that represent this search object.
|
Methods
| Name | Description | |
|---|---|---|
| Aggregate |
Performs all requested aggregates on the objects that match the search criteria.
| |
| AggregateAsync |
Performs all requested aggregates on the objects that match the search criteria asynchronously.
| |
| BinnedSummaryTBin |
Generates a summary broken down by the specified bins for items matching the search criteria.
| |
| BinnedSummaryAsyncTBin |
Generates a summary broken down by the specified bins for items matching the search criteria asynchronously.
| |
| Close |
Closes the search that is cached in the server.
| |
| ConvertAFPatternToRegexPattern |
Convert an AF query pattern to a pattern that can be used with .NET Regex class.
| |
| Equals | Determines whether the specified object is equal to the current object. (Inherited from Object.) | |
| FindObjectFields(String, Int32, Int32) |
This method will return the values as an IList for the specified fields for each of the objects
that match the search tokens.
| |
| FindObjectFieldsTObject(Int32, Int32) |
This method will return the values for the fields defined by the user-defined type for each of the objects
that match the search tokens.
| |
| FindObjectFieldsTObject(String, FuncIListObject, TObject, Int32, Int32) |
This method will return the values as an object for the specified fields for each of the objects
that match the search tokens using the supplied factory delegate.
| |
| FindObjectIds |
This method will return a list of the ID for each object that matches the
search tokens.
| |
| FrequencyDistribution |
Generates a frequency distribution for the specified items.
| |
| FrequencyDistributionAsync |
Generates a frequency distribution for the specified items asynchronously.
| |
| GetHashCode | Serves as the default hash function. (Inherited from Object.) | |
| GetTotalCount |
Returns the total count of the items that could be returned from the search query.
| |
| GetType | Gets the Type of the current instance. (Inherited from Object.) | |
| GroupedSummary |
Generates summaries broken down by group for items matching the search criteria.
| |
| GroupedSummaryAsync |
Generates summaries broken down by group for items matching the search criteria asynchronously.
| |
| HistogramTBin |
Generates a histogram using the specified weighting for items matching this search.
| |
| HistogramAsyncTBin |
Generates a histogram using the specified weighting for items matching this search asynchronously.
| |
| ParseQuery | Obsolete.
Parses the specified search query into search tokens which can be used to search for objects.
| |
| ParseQueryString |
Parses the specified search query into search tokens which can be used to search for objects.
| |
| Refresh |
Refreshes the search that is cached in the server.
| |
| Summary(String, AFSummaryTypes) |
Summarizes the result of this search.
| |
| Summary(String, AFSummaryTypes, String) |
Summarizes the result of this search with the specified weighting.
| |
| SummaryAsync(String, AFSummaryTypes, CancellationToken) |
Summarizes the result of this search asynchronously.
| |
| SummaryAsync(String, AFSummaryTypes, String, CancellationToken) |
Summarizes the result of this search with the specified weighting asynchronously.
| |
| ToString |
Returns a String that represents the current object.
(Overrides ObjectToString.) | |
| TryFindSearchToken | Obsolete.
Find the AFSearchToken associated with the specified AFSearchFilter.
| |
| TryFindSearchTokens | Obsolete.
Find the AFSearchToken list associated with the specified AFSearchFilter.
|
Remarks
The search classes that inherit from this base class perform their searches based on a query string that is converted into search tokens. This provides a flexible way to specify the search query, but not all servers will support every combination of filters. The QuerySearch feature can be checked to determine if query searches are supported by the server. If they are not supported by the server, then the SDK will perform the search using existing search methods in the older servers. If the search is too complex for the older servers, an error will be generated.
A query string is parsed into search tokens which are used to specify the query filters for the search.
To optimize getting items from several pages of search results, a search can be cached by setting the CacheTimeout to a non-zero value. If you will only be getting items from the first page, then it is best to leave the cache disabled. The cache is disabled by default. Call the Refresh method to refresh the results of a cached search. Use the Close method to close a cached search when finished using it to free the memory used by the cached results in the server. Since the search implements the IDisposable interface, you can call the cached search in a using statement to automatically call the Close method when it leaves scope.
| When a query is too complex for a server, then you will get a NotSupportedException. |
Examples
// Get the Database PISystems myPISystems = new PISystems(); PISystem myPISystem = myPISystems.DefaultPISystem; if (myPISystem == null) throw new InvalidOperationException("Default PISystem was not found."); AFDatabase myDB = myPISystem.Databases[dbName]; if (myDB == null) throw new InvalidOperationException("Database was not found."); // Create a search to find all the elements in a specific area // of a plant (e.g. 'WestPlant\Area32') and have either a 'Valve' category or a name beginning with 'Valve'. int count; using (var search = new AFElementSearch(myDB, "FindValves", @"Root:'WestPlant\Area32' (Category:'Valve' OR Name:Valve*)")) { search.CacheTimeout = TimeSpan.FromMinutes(10); // When the elements are returned from a find operation, they are only // partially loaded into memory, typically enough to display their // inherent properties, such as Name, Description, Template, Type, etc. // When a piece of information is accessed in the element that requires more // information, an RPC to the server is made to fully load the element. // By having the search do a full load, all information is loaded in bulk and // we can reduce the number of RPCs made to retrieve this information. count = search.GetTotalCount(); Console.WriteLine("Found {0} Elements.", count); foreach (AFElement item in search.FindObjects(fullLoad: true)) { // Now we can use the elements without having to make any additional RPCs // In the example below, accessing the Attributes collection would have // caused an additional RPC per element found. // Now we can use the elements without having to make any additional RPCs // In the example below, accessing the Attributes collection would have // caused an additional RPC per element found. Console.WriteLine(" Element {0} has {1} Attributes", item.Name, item.Attributes.Count); } }
// Get the Database PISystems myPISystems = new PISystems(); PISystem myPISystem = myPISystems.DefaultPISystem; if (myPISystem == null) throw new InvalidOperationException("Default PISystem was not found."); AFDatabase myDB = myPISystem.Databases[dbName]; if (myDB == null) throw new InvalidOperationException("Database was not found."); // Use attribute value query to find a list of tanks that are // In Service (tanks which are out-of-service have their // Status attribute set to "OS"). int totalCount = 0; using (var search = new AFElementSearch(myDB, "FindTanks", @"Template:'TankAdvanced' |Status:<>'OS'")) { search.CacheTimeout = TimeSpan.FromMinutes(10); // Because we are only going to use a few of the attributes for each // of the elements found, we can reduce the load time and memory space // by just loading the attributes we need. Note that if we inadvertently // use attributes other than these, we will incur a load penalty. AFElementTemplate tankTemplate = myDB.ElementTemplates["Tank"]; AFNamedCollectionList<AFAttributeTemplate> myAttributes = new AFNamedCollectionList<AFAttributeTemplate>(); myAttributes.Add(tankTemplate.AttributeTemplates["Volume"]); myAttributes.Add(tankTemplate.AttributeTemplates["Level"]); myAttributes.Add(tankTemplate.AttributeTemplates["Level|HighLimit"]); myAttributes.Add(tankTemplate.AttributeTemplates["Level|LowLimit"]); // Get today's date and UOMs to be used for converting values. AFTime today = new AFTime("T", CultureInfo.CurrentCulture); UOM bbl = myDB.PISystem.UOMDatabase.UOMs["bbl"]; UOM meter = myDB.PISystem.UOMDatabase.UOMs["m"]; const int pageSize = 1000; int startIndex = 0; IList<Guid> myElementIds = new List<Guid>(pageSize); do { // Find the element IDs and load the attributes we will be using. var results = search.FindObjectIds(startIndex, pageSize: pageSize); myElementIds.Clear(); int c = 0; foreach (Guid id in results) { totalCount++; myElementIds.Add(id); c++; if (c >= pageSize) break; } if (myElementIds.Count == 0) break; AFNamedCollectionList<AFElement> elements = AFElement.LoadAttributes(myPISystem, myElementIds, myAttributes); // Once the elements are loaded, we can process them. foreach (AFElement element in elements) { try { AFAttributeList attributes = new AFAttributeList(); attributes.Add(element.Attributes["Volume"]); attributes.Add(element.Attributes["Level"]); attributes.Add(element.Attributes["Level|HighLimit"]); attributes.Add(element.Attributes["Level|LowLimit"]); AFValues values = attributes.GetValue(today); if (values[0].IsGood && values[1].IsGood && values[2].IsGood && values[3].IsGood) { double volume = (double)values[0].Convert(bbl).Value; double level = (double)values[1].Convert(meter).Value; double high = (double)values[2].Convert(meter).Value; double low = (double)values[3].Convert(meter).Value; Console.WriteLine("Tank Inventory for '{0}' is {1} bbl. %Full={2}", element.Name, volume, 100 * (high - low) / level); } else { Console.WriteLine("Bad data in Tank '{0}'", element.Name); } } catch (FormatException) { Console.WriteLine("Error in Tank '{0}'", element.Name); } } startIndex += myElementIds.Count; // Advance to next page. } while (myElementIds.Count > 0 && myElementIds.Count >= pageSize); } Console.WriteLine("Processed {0} Elements.", totalCount);
1//************************************************************************* 2/// <summary> 3/// This class defines the object fields returned from the 4/// FindObjectFields method. 5/// </summary> 6public class EventFrameFields 7{ 8 // Field mapped using default name. 9 public Guid ID; 10 // Property mapped using default name. 11 public string Name { get; set; } 12 // Field mapped using 'ObjectField' attribute. 13 [AFSearch.ObjectField("Duration")] 14 public AFTimeSpan EFDuration; 15 // Property mapped using 'ObjectField' attribute. 16 [AFSearch.ObjectField("StartTime")] 17 public AFTime EFStart { get; set; } 18 // Attribute value mapped to property using 'ObjectField' attribute. 19 [AFSearch.ObjectField("|Level")] 20 public AFValue Level { get; set; } 21} 22 23//*************************************************************************
// Get the Database PISystems myPISystems = new PISystems(); PISystem myPISystem = myPISystems.DefaultPISystem; if (myPISystem == null) throw new InvalidOperationException("Default PISystem was not found."); AFDatabase myDB = myPISystem.Databases[dbName]; if (myDB == null) throw new InvalidOperationException("Database was not found."); // Create a search to find all the event frames created from the 'Event' // template and its 'Level' attribute value is less than 90. int count; using (var search = new AFEventFrameSearch(myDB, "FindEventFields", @"Template:'Event' |Level:<90.0")) { search.CacheTimeout = TimeSpan.FromMinutes(10); // Do the search // Return specified fields as object list. count = 0; Console.WriteLine("Find Object Fields:"); foreach (var row in search.FindObjectFields("ID Name StartTime EndTime |Level")) { count++; foreach (var item in row) { Console.Write("{0}, ", item); } Console.WriteLine(); } Console.WriteLine("Found {0} EventFrames.", count); // Return specified fields as an anonymous type. count = 0; var foundItems2 = search.FindObjectFields("ID Name StartTime EndTime |Level", i => new { ID = i[0], Name = i[1], Start = i[2], End = i[3], Value = i[4] }); Console.WriteLine("Find Object Fields using Anonymous Type:"); foreach (var row in foundItems2) { count++; Console.WriteLine("ID={0}, N='{1}', ST={2}, ET={3}, V={4}", row.ID, row.Name, row.Start, row.End, row.Value); } Console.WriteLine("Found {0} EventFrames.", count); // Return event frame name as list of strings. count = 0; var foundItems3 = search.FindObjectFields("Name", i => i[0].ToString()); Console.WriteLine("Find Object Names as list of strings:"); foreach (var row in foundItems3) { count++; Console.WriteLine(row); } Console.WriteLine("Found {0} EventFrames.", count); // // Return event frame security tokens as list and check security for current user. count = 0; var foundItems4 = search.FindObjectFields("SecurityToken", i => (AFSecurityRightsToken)i[0]); Console.WriteLine("Find Object SecurityTokens and Check Security:"); foreach (var tokenList in foundItems4.ChunkedBy(500)) { // Check Security using Windows Identity. var rights = AFSecurity.CheckSecurity(myPISystem, WindowsIdentity.GetCurrent(), tokenList); foreach (var rightsItem in rights) { Console.WriteLine($" Security Rights for '{myPISystem.CurrentUserName}': {rightsItem.Key} = {rightsItem.Value}"); } // Check Security using Identities. rights = AFSecurity.CheckSecurity(myPISystem, myPISystem.CurrentUserIdentities, tokenList, myPISystem.CurrentUserName); foreach (var rightsItem in rights) { Console.WriteLine($" Security Rights for '{myPISystem.CurrentUserIdentityString}': {rightsItem.Key} = {rightsItem.Value}"); } count += tokenList.Count; } Console.WriteLine("Found {0} EventFrames.", count); // Return specified fields using dynamic type conversion. count = 0; var foundDynItems = search.FindObjectFields<EventFrameFields>(); Console.WriteLine("Find Object Fields using Dynamic Type Conversion:"); foreach (var row in foundDynItems) { count++; Console.WriteLine("ID={0}, N='{1}', ST={2}, D={3}, V={4}", row.ID, row.Name, row.EFStart, row.EFDuration, row.Level); } Console.WriteLine("Found {0} EventFrames.", count); }
1// Get the Database 2PISystems myPISystems = new PISystems(); 3PISystem myPISystem = myPISystems.DefaultPISystem; 4if (myPISystem == null) 5 throw new InvalidOperationException("Default PISystem was not found."); 6AFDatabase myDB = myPISystem.Databases[dbName]; 7if (myDB == null) 8 throw new InvalidOperationException("Database was not found."); 9 10// Create a search to find all the event frames created from the 'Event' 11// template, with a start time greater than or equal to three days ago, 12// and its 'Level' attribute value is greater than or equal 45. 13// Note: Time strings must be in a format supported by AFTime.Parse which 14// will use current culture and then fall back to invariant culture. 15int count; 16using (var search = new AFEventFrameSearch(myDB, "FindEvents", @"Template:'Event' Start:>='*-3d' |Level:>=45.0")) 17{ 18 search.CacheTimeout = TimeSpan.FromMinutes(10); 19 20 // When the event frames are returned from a find operation, they are only 21 // partially loaded into memory, typically enough to display their 22 // inherent properties, such as Name, Description, Template, etc. 23 // When a piece of information is accessed in the event frame that requires more 24 // information, an RPC to the server is made to fully load the event frame. 25 // By having the search do a full load, all information is loaded in bulk and 26 // we can reduce the number of RPCs made to retrieve this information. 27 count = search.GetTotalCount(); 28 Console.WriteLine("Found {0} EventFrames.", count); 29 foreach (AFEventFrame item in search.FindObjects(fullLoad: true)) 30 { 31 // Now we can use the event frames without having to make any additional RPCs 32 // In the example below, accessing the Attributes collection would have 33 // caused an additional RPC per element found. 34 // Now we can use the event frames without having to make any additional RPCs 35 // In the example below, accessing the Attributes collection would have 36 // caused an additional RPC per event frame found. 37 Console.WriteLine(" EventFrame {0} has {1} Attributes", item.Name, item.Attributes.Count); 38 } 39}