(X) Hide this
    • Login
    • Join
      • Generate New Image
        By clicking 'Register' you accept the terms of use .

Windows 8.1: The Windows Search API

(2 votes)
Andrea Boschin
>
Andrea Boschin
Joined Nov 17, 2009
Articles:   91
Comments:   9
More Articles
1 comments   /   posted on Jan 15, 2014
Categories:   Windows 8

Tweet

Starting from Windows 8.1, the search has slightly changed in the Windows Store apps. The old Search contract is not anymore the sole way to go, but now the app is enabled to provide its own search box. To give a better search experience, it has now the ability of integrating with the Windows Search engine with some new APIs. This imply some more work for the developer that has to load in the search engine the items he wants to make searchable, but the results is much more interesting and effective.

The Windows.Storage.Search namespace

Inside this new namespace, it is now included the whole toolset you can use to provide an effective search. These APIs let you to add and remove items from the indexing engine attaching some properties to each item and then use some queries to retrieve this content from the engine itself.

There are two classes that collaborates in this work: 

ContentIndexer: it is the class that represents the engine where you will load the indexed items. It is a sealed class and you cannot create instances of it. You can only retrieve an instance using the GetIndexer method.

IndexableContent: it represents an item you can add, update and finally remove from the indexing engine. Each items has an unique Id you can use to retrieve the items after having added them.

So, the first thing to do to interact with the engine is to get the indexer's instance. This is done using the GetIndexer method, without parameters in the simplest case. You can also provide a name and this enables you to have more than an indexer, each one with its separated items. Always remember that the indexer, with or without a name, has a scope related to the local application and cannot be accessed by other apps.

   1: // retrieve the default indexer of the app
   2: ContentIndexer defaultIndexer = ContentIndexer.GetIndexer();
   3:  
   4: // retrieve a secondary indexer of the app
   5: ContentIndexer otherIndexer = ContentIndexer.GetIndexer("otherIndexer");

Be aware that your store will survive when the app is closed. You can see the current revision of its content using the progressive incremental value of the Revision property. Once you get your indexer you have a couple of methods to manipulate the items in the store.

AddAsync : adds an item to the store
UpdateAsync: retrieve an item and update its properties using the Id property
DeleteAsync, DeleteMultipleAsync and DeleteAllAsync: provide some ways to remove content from the store

To add an item, as an example, you have first to create the IndexableContent's instance. This means you have to create an instance of the class, provide an unique Id, populate its properties you want to make available and finally add a stream of the item content. The following code shows the creation of an element and its addition to the store:

   1: using (var stream = new InMemoryRandomAccessStream())
   2: {
   3:     // write the content to the stream
   4:     var writer = new DataWriter(stream);
   5:     writer.WriteString(content);
   6:     await writer.StoreAsync();
   7:     // reset the stream to its beginning
   8:     stream.Seek(0);
   9:  
  10:     // create the item to add
  11:     IndexableContent item = new IndexableContent
  12:     {
  13:         Id = Guid.NewGuid().ToString(),
  14:         Stream = stream,
  15:         StreamContentType = "text/plain"
  16:     };
  17:  
  18:     // and finally add it
  19:     await otherIndexer.AddAsync(item);
  20: }

The item content is represented by a stream and you can use a DataWriter to generate it from the original string. At the end the stream is rewinded to the beginning so the indexable content can read from it.

Each item you can add can be decorated with a number of additional properties. These properties are used to search the items. A property is a name-value pair and you can use the SystemProperties class to find the correct names. The class contains a number of basic properties and some other that are specific for multimedia scenario. As an example you can provide the Encoding bitrate of an audio stream using the SystemProperties.Audio.EncodingBitrate. Here are how to add properties to the previous indexed content.

   1: item.Properties.Add(SystemProperties.ItemNameDisplay, "LoremIpsum");
   2: item.Properties.Add(SystemProperties.Keywords, "Lorem, ipsum, dolor, sit, amet");
   3: item.Properties.Add(SystemProperties.Comment, "The same old lorem ipsum");
   4: item.Properties.Add(SystemProperties.Title, "Lorem ipsum dolor sit amet");
   5: item.Properties.Add(SystemProperties.Media.Year, DateTime.Now.Year);

Another important property you have to handle is the language. Providing the correct language is important because the indexing engine can use it to correctly spell the properties and give a better result. It is also important to not provide a language is you do not really know the correct language of your resource. This because the engine will try to guess it analyzing the value.

   1: var comment = new ValueAndLanguage();
   2: comment.Language = "en-US";
   3: comment.Value = "The same old lorem ipsum";
   4: item.Properties.Add(SystemProperties.Comment, comment);
   5:  
   6: await otherIndexer.AddAsync(item);

Performing a search

Searching content is definitely easy, but you have a number of options to work with. A search is identified by a query that you create providing the search terms. Once the query has been created using the CreateQuery method, you execute it by calling the GetAsync method.

   1: var query = otherIndexer.CreateQuery("lorem", new string[] { SystemProperties.ItemNameDisplay });
   2: IEnumerable<IIndexableContent> result = await query.GetAsync();

As you can see the creation of the query requires the filter (in this case a simple word) and a list of properties you need to retrieve. It is important not to ask for more properties that the ones you really need to avoid unuseful works for the engine.

The query provides also the ability to order the returned item by some properties. This may be achieved using an additional overload of the CreateQuery method:

   1: var query = otherIndexer.CreateQuery("lorem",
   2:     new string[] { SystemProperties.ItemNameDisplay },
   3:     new SortEntry[] 
   4:     { 
   5:         new SortEntry{ AscendingOrder = true, PropertyName = SystemProperties.Media.Year }
   6:     });
   7:  
   8: IEnumerable<IIndexableContent> result = await query.GetAsync();

The query has additional methods you can use to improve your searches. You can retrieve the count of items without retrieving the items using the GetCountAsync ad finally retrieve only the properties with the GetPropertiesAsync.

Until now we have passed a query made of a single word and the engines searches it into all the properties. You can also create a query using a special language called AQS (http://msdn.microsoft.com/en-us/library/bb266512). This language allow to specify a more complex grammar that enable searches on specific properties and the use of operators.

The following example searches using an AQS query:

   1: var query = otherIndexer.CreateQuery("keyword:(lorem OR ipsum)", new string[] { SystemProperties.ItemNameDisplay });
   2: IEnumerable<IIndexableContent> result = await query.GetAsync();

The query specified searches the SystemProperties.Keywords values for items that contains the words “lorem” or “ipsum”. The query language has lot of Values you can search for.

System.Size : evaluate the size of the item
System.DateModified: enable search of items on the basis of the modification date and time

Using appcontent-ms files

If the toolset I’ve explained until now does not suffices, be aware that you have also another option for feeding the indexing engine. This uses a set of files, stored into a folder in the file system. You have to create the folder and add to it the files you want to be indexed by the engine. Each file represents an item in the index and it contains all the properties you can use in the previous samples beside with the content itself. Here is a sample file:

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <IndexerSampleInformation>
   3:   <Properties xmlns="http://schemas.microsoft.com/Search/2013/ApplicationContent">
   4:     <Name>LoremIpsum</Name>
   5:     <Keywords>
   6:       <Keyword xml:lang="en-US">Lorem</Keyword>
   7:       <Keyword xml:lang="en-US">ipsum</Keyword>
   8:       <Keyword xml:lang="en-US">dolor</Keyword>
   9:       <Keyword xml:lang="en-US">sit</Keyword>
  10:       <Keyword xml:lang="en-US">amet</Keyword>
  11:     </Keywords>
  12:     <Comment>The same old lorem ipsum</Comment>
  13:     <AdditionalProperties>
  14:       <Property Key="System.Title">Lorem ipsum dolor sit amet</Property>
  15:     </AdditionalProperties>
  16:   </Properties>
  17:   <IndexerSampleSpecificElement sc:IndexableContent="true" xmlns:sc="http://schemas.microsoft.com/Search/2013/ApplicationContent">
  18:     Lorem ipsum dolor sit amet, consectetur adipiscing elit.
  19:     Morbi et orci fringilla libero vestibulum luctus.
  20:     Aliquam lacus neque, vehicula eget scelerisque sed, tristique rhoncus felis.
  21:   </IndexerSampleSpecificElement>
  22: </IndexerSampleInformation>

You can embed this files in your solution and copy it to the folder using the common file IO API but you can also create them at runtime. Once the files has been added you are able to create a query using a set of specific APIs:

   1: StorageFolder folder = await ApplicationData.Current.LocalFolder.CreateFolderAsync("Indexed", CreationCollisionOption.OpenIfExists);
   2:  
   3: QueryOptions options = new QueryOptions();
   4: options.IndexerOption = IndexerOption.OnlyUseIndexer;
   5: options.ApplicationSearchFilter = Windows.Storage.SystemProperties.ItemNameDisplay + ":\"LoremIpsum\"";
   6:  
   7: StorageFileQueryResult query = folder.CreateFileQueryWithOptions(options);
   8:  
   9: IEnumerable<StorageFile> foundFiles = await query.GetFilesAsync();
  10:  
  11: var found = foundFiles.Count();

This way is much more convenient to keep in sync items from some source and the indexed items. You only have to create a file every time an item is added and remove it at the right moment. You are able to perform the same operations you can do with the previous approach.


Subscribe

Comments

  • Jonathantrott

    Re: Windows 8.1: The Windows Search API


    posted by Jonathantrott on Aug 06, 2014 14:17
    windows 8.1 is more compatible as compared to windows 8 because it has more features and information the no one else. professional assignment writers

Add Comment

Login to comment:
  *      *       

From this series