Administrate Indexing Server from Within Your Application

Introduction

You can leverage Microsoft Indexing Server to provide powerful search capabilities within your application. Indexing Server can be used to index files on your local or network file system as well as files of your local Web site. You then can query the Indexing catalog by using a standard SQL syntax. The following article describes in detail how you can create and configure your own Indexing catalog and then search it from within your application. This article concentrates on how you can programmatically administrate Indexing Server; for example, create a new Indexing catalog and then add folders to be indexed by this catalog.

The Indexing Service Administration API

Current versions of the .NET framework do not provide any types to programmatically administrate the Indexing Server. But Indexing Server provides a COM-based API you can use. Add a reference to the "Indexing Service Administration Type Library 1.0" that you find under the COM tab of the "Add Reference" dialog. This references the "ciodm.dll" in the windows\system32 folder. Next, import the namespace "CIODMLib"; it will give you access to three types:

  • AdminIndexServerClass—This type is used to administrate the Indexing Server itself. It allows to create or remove catalogs, start or stop the Indexing service, etc.
  • CatAdmClass—This type provides access to an existing Indexing catalog. You can add or remove search scopes, start or stop the catalog, etc.
  • ScopeAdm—This type provides access to a search scope. You can set logon information for the scope, start a full or incremental scan, etc.

How to Retrieve the List of Indexing Catalogs

Indexing Server can have many Indexing catalogs and your application always tells Indexing Server which catalog to search. You can use the AdminIndexServerClass type to enumerate all defined Indexing catalogs. First, you need to create an instance of the AdminIndexServerClass type. Then, you call FindFirstCatalog() to find the first catalog in the list. It returns true if it finds a catalog; otherwise, false. To find the next catalog, you call FindNextCatalog() that again returns true when it finds another catalog, or false otherwise. As the example below shows, you first call FindFirstCatalog() and then in a loop FindNextCatalog() until one of them returns false. Each time, you then call GetCatalog() to get a reference to the current catalog in the list. This returns an object of the type object and you need to query for the appropriate interface on that object. Simple type casting will not work because you are working with COM objects. You need to use the "as" keyword followed by the interface you are looking for. Underneath, this will query for the appropriate interface on that COM object and return null if not present or a reference to the interface. The interface you are looking for is the ICatAdm interface that provides you access to the catalog itself. The CatAdm type itself implements also the ICatAdm interface.

protected void FillCatalogList(ListView ListOfCatalogs)
{
   AdminIndexServerClass Admin = new AdminIndexServerClass();

   // remove any existing item in the catalog list
   ListOfCatalogs.Items.Clear();

   // finds the first catalog in the list
   bool FoundCatalog = Admin.FindFirstCatalog();

   // loop through all catalogs present
   while (FoundCatalog)
   {
      // gets the object representing the current catalog
      ICatAdm Catalog = Admin.GetCatalog() as ICatAdm;

      // adds the catalog details to the list view
      ListViewItem Item =
         ListOfCatalogs.Items.Add(Catalog.CatalogName);
      Item.SubItems.Add(Catalog.CatalogLocation);
      Item.SubItems.Add(Catalog.IsUpToDate.ToString());
      Item.SubItems.Add(Catalog.DocumentsToFilter.ToString());
      Item.SubItems.Add(Catalog.FilteredDocumentCount.ToString());
      Item.SubItems.Add(Catalog.DelayedFilterCount.ToString());
      Item.SubItems.Add(Catalog.FreshTestCount.ToString());
      Item.SubItems.Add(Catalog.IndexSize.ToString());
      Item.SubItems.Add(Catalog.PctMergeComplete.ToString());
      Item.SubItems.Add(Catalog.PendingScanCount.ToString());
      Item.SubItems.Add(Catalog.PersistentIndexCount.ToString());
      Item.SubItems.Add(Catalog.QueryCount.ToString());
      Item.SubItems.Add(Catalog.StateInfo.ToString());
      Item.SubItems.Add(Catalog.TotalDocumentCount.ToString());
      Item.SubItems.Add(Catalog.UniqueKeyCount.ToString());
      Item.SubItems.Add(Catalog.WordListCount.ToString());

      // finds the next catalog in the list
      FoundCatalog = Admin.FindNextCatalog();
   }

   // select the first catalog in the list
   if (ListOfCatalogs.Items.Count > 0)
      ListOfCatalogs.SelectedIndices.Add(0);
}

As the example above shows, the ICatAdm interface provides a number of properties about the catalog. You can obtain the name, the location, whether the catalog is up to date or not, and so forth. The example above adds each found catalog to a list view.

How to Start, Stop, Pause, or Continue Indexing Server

The AdminIndexServerClass provides four methods to start, stop, pause, and continue the Indexing service. It also provides methods to find out whether the service is running or paused:

  • Start()—Starts the Indexing service
  • Stop()—Stops the Indexing service
  • Pause()—Pauses the Indexing service
  • Continue()—Continues a paused Indexing service
  • IsRunning()—Returns true if the Indexing service is running
  • IsPaused()—Returns true if the Indexing service is paused

How to Add or Remove an Indexing Catalog

You can add a new catalog with the AddCatalog() method. You need to pass along the name of the new catalog as well as the folder location where the catalog will be created. You need to restart the Indexing service before you can use a newly created catalog. The following example shows a dialog so the user can enter the name and location of a new catalog. Next, it calls AddCatalog and then asks the user whether to restart the Indexing service so the catalog becomes usable. Finally, it calls the method shown above to re-query the list of defined catalogs—FillCatalogList().

private void AddCatalog_Click(object sender, EventArgs e)
{
   // the dialog to create a new catalog
   AddNewCatalog AddNew = new AddNewCatalog();

   // we want to create the catalog
   if (AddNew.ShowDialog(this) == DialogResult.OK)
   {
      AdminIndexServerClass Admin = new AdminIndexServerClass();

      // create the new catalog
      Admin.AddCatalog(AddNew.NewCatalogName,
                       AddNew.NewCatalogLocation);

      // restart the service for the new catalog to function
      if (MessageBox.Show(this, RestartService, this.Text,
         MessageBoxButtons.YesNo, MessageBoxIcon.Question) ==
            DialogResult.Yes)
      {
         Admin.Stop();
         Admin.Start();
      }

      // refresh the list of catalogs
      FillCatalogList(ListOfCatalogs);
   }
}

You can call RemoveCatalog() to remove an existing catalog. You need to pass along the name of the catalog. You also need to first stop the Indexing service to be able to remove any catalog. The example below asks whether the user wants to delete the currently selected catalog. If the user confirms, it stops the Indexing services, deletes the catalog, and then starts the Indexing service again. Afterwards, it calls FillCatalogList() again to refresh the list of catalogs in the list view.

private void RemoveCatalog_Click(object sender, EventArgs e)
{
   string CatalogName = ListOfCatalogs.SelectedItems[0].Text;

   // ask the user to confirm the deletion of the catalog
   if (MessageBox.Show(this, ConfirmDeleteCatalog, CatalogName,
      MessageBoxButtons.YesNo, MessageBoxIcon.Question) ==
         DialogResult.Yes)
   {
      AdminIndexServerClass Admin = new AdminIndexServerClass();

      Admin.Stop();
      Admin.RemoveCatalog(CatalogName, true);
      Admin.Start();

      // refresh the list of catalogs
      FillCatalogList(ListOfCatalogs);
   }
}

Administrate Indexing Server from Within Your Application

How to Retrieve the List of Search Scopes of a Catalog

Each catalog has search scopes, which are the folders which are included in the catalog. A search scope can be inclusive (by default) or exclusive, meaning this folder will not be included in the catalog. With exclusive you can define sub-folders that should be excluded by this catalog. Listing all search scopes of catalog works very similarly as listing all catalogs of the Indexing Server. First, obtain a reference to the catalog itself by calling the GetCatalogByName() method on an instance of the AdminIndexServerClass type. This again returns an object of the type object and you need to query for the ICatAdm interface using the "as" keyword.

private ICatAdm GetCatalog(string CatalogName)
{
   AdminIndexServerClass Admin = new AdminIndexServerClass();

   // find the catalog by name
   return Admin.GetCatalogByName(CatalogName) as ICatAdm;
}

Next, you call FindFirstScope() on the ICatAdm interface to get the first search scope. It returns true if one is found; otherwise, false. You call FindNextScope() on the ICatAdm interface to find subsequent search scopes, which again returns true when one is found; false otherwise. You do this again in a loop until one returns false. Each time, you call GetScope() to obtain a reference to the current search scope. This again returns an object of the type object and you query for the IScopeAdm interface via the "as" keyword. That interface then provides a list of properties about the search scope itself:

private void FillScopeList(string CatalogName)
{
   // remove any existing item in the scope list
   ListOfScopes.Items.Clear();

   // get a handle to the newly selected catalog
   ICatAdm Catalog = GetCatalog(CatalogName);

   // search for the first scope item
   bool FoundScope = Catalog.FindFirstScope();

   // loop through all the scope items
   while (FoundScope)
   {
      // get a handle to the current scope item
      IScopeAdm Scope = Catalog.GetScope() as IScopeAdm;

      // create a new list view item and set its values
      ListViewItem Item = ListOfScopes.Items.Add(Scope.Alias);
      Item.SubItems.Add(Scope.ExcludeScope.ToString());
      Item.SubItems.Add(Scope.Logon);
      Item.SubItems.Add(Scope.Path);
      Item.SubItems.Add(Scope.VirtualScope.ToString());

      // search for the next scope item
      FoundScope = Catalog.FindNextScope();
   }

   // select the first scope in the list
   if (ListOfScopes.Items.Count > 0)
      ListOfScopes.SelectedIndices.Add(0);
}

The example above adds all search scopes of a catalog to a list view. This includes the path of the search scope and whether it is exclusive. The VirtualScope is set only if this catalog indexes a Web site.

How to Start, Stop, Pause, or Continue a Catalog

You can also start, stop, pause, or continue an individual catalog itself. First, you need to get a reference to the catalog by calling GetCatalogByName on an instance of the AdminIndexServerClass type. Then, you query again for the ICatAdm interface. The ICatAdm interface also provides methods to find out whether the catalog is running or paused:

  • StartCatalog()—Starts the Indexing catalog
  • StopCatalog()—Stops the Indexing catalog
  • PauseCatalog()—Pauses the Indexing catalog
  • ContinueCatlog()—Continues a paused Indexing catalog
  • IsCatalogRunning()—Returns true if the Indexing catalog is running
  • IsCatalogPaused()—Returns true if the Indexing catalog is paused

How to Add or Remove a Search Scope to a Catalog

First, you again need to obtain a reference to the catalog and then call the method AddScope(). You pass along the folder to index and true if the search scope is exclusive or false if it is inclusive. The example below first shows a dialog so the user can enter the folder of the search scope and select whether it is exclusive or inclusive. Next, it gets the name of the currently selected catalog, obtains the catalog object for it, and then adds the search scope by calling AddScope() on the catalog object. It then asks the user whether a full scan for it should be performed. In that case, it gets a reference to the newly created search scope by calling GetScopeByPath() on the catalog object and then calls Rescan() on it, passing along true for a full scan. Finally, it calls the FillScopeList() method to refresh the list of search scopes in the list view.

private void AddScope_Click(object sender, EventArgs e)
{
   string CatalogName = ListOfCatalogs.SelectedItems[0].Text;

   // the dialog to create a new scope
   AddNewScope AddNew = new AddNewScope();

   // we want to create the scope
   if (AddNew.ShowDialog(this) == DialogResult.OK)
   {
      // get the catalog handle
      ICatAdm Catalog = GetCatalog(CatalogName);

      // create the new scope
      Catalog.AddScope(AddNew.Path, AddNew.ExcludeFolder,
                       null, null);

      // ask the user if we want to scan the new search scope
      if (MessageBox.Show(this, PerformFullScan, this.Text,
         MessageBoxButtons.YesNo, MessageBoxIcon.Question) ==
            DialogResult.Yes)
      {
         // get a reference to the search scope we just added
         IScopeAdm Scope = Catalog.GetScopeByPath(AddNew.Path)
                           as IScopeAdm;

         // to a full scan
         Scope.Rescan(true);
      }

      // refresh the list of scopes
      FillScopeList(ListOfCatalogs.SelectedItems[0].Text);
   }
}

Adding or removing search scopes does not require stopping or restarting the catalog service. To remove a search scope, you call the RemoveScope() method on the catalog object and pass along the path of the search scope. The example below asks whether the user wants to delete the currently selected search scope. If the user confirms, it deletes the search scope and afterwards it again calls FillScopeList() to refresh the list of search scopes in the list view.

private void RemoveScope_Click(object sender, EventArgs e)
{
   string CatalogName = ListOfCatalogs.SelectedItems[0].Text;
   string ScopePath = ListOfScopes.SelectedItems[0].SubItems[3].Text

   // ask the user to confirm the deletion of the search scope
   if (MessageBox.Show(this, ConfirmDeleteScope, ScopePath,
      MessageBoxButtons.YesNo, MessageBoxIcon.Question) ==
         DialogResult.Yes)
   {
      // get the catalog handle
      ICatAdm Catalog = GetCatalog(CatalogName);

      // delete the selected scope
      Catalog.RemoveScope(ScopePath);

      // refresh the list of scopes
      FillScopeList(ListOfCatalogs.SelectedItems[0].Text);
   }
}

Summary

The administrative API of Indexing Server is very simple. The AdminIndexServerClass type gives you full control over the Indexing Server itself. The ICatAdm interface and CatAdm type provide full control over each catalog. The IScopeAdm interface and ScopeAdm type provide full control over each search scope. This makes it very easy for you to add new catalogs and search scopes or remove existing catalogs and search scopes programmatically. You also have the ability to force a full or partial scan of search scopes so that additions or changes are available to your application instantly. Refer to the MSDN help for more details about the Indexing Server API. The enclosed sample application provides an example administrative interface to Indexing Server using the very same API. If you have comments on this article or this topic, please contact me @ klaus_salchner@hotmail.com. I want to hear if you learned something new. Contact me if you have questions about this topic or article.

About the Author

Klaus Salchner has worked for 14 years in the industry, nine years in Europe and another five years in North America. As a Senior Enterprise Architect with solid experience in enterprise software development, Klaus spends considerable time on performance, scalability, availability, maintainability, globalization/localization, and security. The projects he has been involved in are used by more than a million users in 50 countries on three continents.

Klaus calls Vancouver, British Columbia his home at the moment. His next big goal is running the New York marathon in 2006. Klaus is interested in guest speaking opportunities or as an author for .NET magazines or Web sites. He can be contacted at klaus_salchner@hotmail.com or http://www.enterprise-minds.com.

Enterprise application architecture and design consulting services are available. If you want to hear more about it, contact me! Involve me in your projects and I will make a difference for you. Contact me if you have an idea for an article or research project. Also, contact me if you want to co-author an article or join future research projects!



Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Java developers know that testing code changes can be a huge pain, and waiting for an application to redeploy after a code fix can take an eternity. Wouldn't it be great if you could see your code changes immediately, fine-tune, debug, explore and deploy code without waiting for ages? In this white paper, find out how that's possible with a Java plugin that drastically changes the way you develop, test and run Java applications. Discover the advantages of this plugin, and the changes you can expect to see …

  • The Red Hat® Standard Operating Environment SOE helps you define, deploy, and maintain Red Hat Enterprise Linux® and third-party applications as an SOE. The SOE is fully aligned with your requirements as an effective and managed process, and fully integrated with your IT environment and processes. Benefits of an SOE: SOE is a specification for a tested, standard selection of computer hardware, software, and their configuration for use on computers within an organization. The modular nature of the Red …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds