Click here to Skip to main content
Click here to Skip to main content
Go to top

Peer Graph - Searching

, 6 Jan 2006
Rate this:
Please Sign up or sign in to vote.
Searching record meta-data in a Peer Graph using Microsoft's Peer-to-Peer technology.

Background

Microsoft's Peer-to-Peer Graphing technology provides a stable, reliable, and robust infrastructure for Windows peer-to-peer applications to communicate. Peers use the Peer Name Resolution Protocol (PNRP - a serverless DNS) to register and discover other peers within the graph. Graphs are the foundation for connecting peers, services, and resources within a peer network. A peer can be a user-interactive application, service, or resource. Graphing allows data to be passed between peers efficiently and reliably.

Microsoft's entire Peer-to-Peer technology is exposed through the latest Platform SDK as C/C++ API calls. However, the code in this article shows these APIs being used from .NET managed code using C#.

Introduction

This article introduces the concept of searching meta-data within peer records published to a peer-to-peer graph. As we learned in the previous article, meta-data describing the contents or purpose of the object being published is attached to each record. Microsoft refers to this meta-data as attributes. Attributes are represented as well-formed XML fragments.

In their wisdom, Microsoft again decided to use XML to represent criteria used for issuing search requests. Microsoft provides a simple schema against which the XML search criteria fragments must conform. This article focuses on the PeerSearch class which wraps the basic search functionality supported by the unmanaged APIs.

Search Criteria Schema

The search criteria schema is very simple. The root element must be called <peersearch>. It supports two types of operations: <and> and <or>. Operators can be nested and contain a <clause>. Each <clause> has three XML attributes: name, type and comparison. The name attribute represents the name of the attribute, and type its data type. Only three data types are supported: int, date, and string. Six types of comparisons are supported; equal, greater-than, less-than, not-equal, greater-than-or-equal, and less-than-or-equal. The clause's value is specified between the beginning and ending tags. Here is a simple example:

<peersearch>
   <or>
      <clause attrib="peercreatorid" 
              type="string" 
              compare="equal">James *</clause>
      <and>
         <clause attrib="peerlastmodificationtime" 
              type="date" 
              compare="greater">2003-01-31</clause>
         <clause attrib="peerlastmodificationtime" 
              type="date" 
              compare="less">2003-02-28</clause>
      </and>
   </or>
</peersearch>

Simple pattern matching is supported using * and ?. These characters can be escaped using the \ character.

The value of an attribute uses a case-insensitive comparison against the fields in the meta-data. That is, attrib="peercreatorid" matches against the meta-data <attribute name="PeerCreatorID" ...>. The value of each clause is also case-insensitive. That is, <clause ...>James *</clause> matches the meta-data <attribute>james bond</attribute> and <attribute>JAMES BROWN</attribute>.

The values of the type and compare attributes are case-sensitive since these come from the XML schema. When a date type is expected, you must use the standard XML format for date and time.

The underlying peer graphing API reserves the following list of attributes.

  • peerlastmodifiedby
  • peercreatorid
  • peerlastmodificationtime
  • peerrecordid
  • peerrecordtype
  • peercreationtime
  • peerlastmodificationtime

While a record's meta-data won't contain these attributes, these names can still be used in the search criteria to match the corresponding properties of a PEER_RECORD object.

The SearchCriteria.xsd file included in the sample code contains the schema for the XML search criteria. It's copied from the MSDN library web page that describes the Record Search Query Format. This schema is used by the Validate method described below.

Creating a Search Object

A new CreateSearch method has been added to the PeerGraph class that supports creating a new search object. By default, an instance of a new PeerSearch class is returned. You can override the virtual OnCreateSearch method to return your own, better search object since the default one is very basic.

public PeerSearch CreateSearch()
{
  return OnCreateSearch();
}

protected virtual PeerSearch OnCreateSearch()
{
  return new PeerSearch(this);
}

The PeerSearch constructor takes a single Graph parameter that is the PeerGraph against which the search will be performed.

PeerSearch Class

The PeerSearch class includes an Xml property for setting the XML search criteria fragment to use for searching. This class also overrides the ToString method to return the XML search criteria fragment. The two remaining methods are described in the following sections.

Search Method

The Search method is virtual so it can be overridden. Its default implementation calls an internal method of PeerGraph to perform the search.

public virtual PeerRecordCollection Search()
{
  return Graph.Search(xml);
}

The internal PeerGraph method calls the underlying PeerGraphSearchRecords API. If the search is valid, the results are returned in a PeerRecordCollection collection. This collection supports the standard foreach enumeration.

internal PeerRecordCollection Search(string Criteria)
{
  IntPtr hPeerEnum;
  uint hr = PeerGraphNative.PeerGraphSearchRecords(hGraph, 
                                 Criteria, out hPeerEnum);
  if (hr != 0) throw new PeerGraphException(hr);

  return new PeerRecordCollection(hGraph, hPeerEnum);
}

Validate Method

The sample file SearchCriteria.xsd contains the schema for validating XML search criteria fragments, and is stored as an embedded resource in the sample application. This schema is loaded and compiled as a static (shared) field of the PeerSearch class. The Validate method uses the schema to validate the currently stored XML fragment.

public bool Validate()
{
  XmlValidatingReader vr = new XmlValidatingReader(Xml, 
                               XmlNodeType.Element, null);
  vr.Schemas.Add(schema,null);
  bool valid;
  try
  {
    while (vr.Read()) { }
    valid = true;
  }
  catch (XmlSchemaException ex)
  {
    valid = false;
  }
  catch (XmlException ex)
  {
    valid = false;
  }
  return valid;
}

The Validate method returns true if the XML fragment matches the schema, otherwise, false if an exception is thrown. There should never be a need to validate the XML fragment, but this feature is included for completeness.

Using the Sample Application

The sample application lets you first create a graph (unsecured peer name 0.SearchSharedFiles) with an initial identity. The first instance should be opened with this identity. It will pause a few seconds looking for other instances, then begin to listen. Each subsequent instance of the application should open the graph with a different identity. These instances will connect to the nearest peer and synchronize. Each instance of the application is a peer.

On the Share tab, use the Add Folder button to select a folder containing files. After selecting a folder, each file in the folder is published to the graph as a record. Note that the record is set to expire after 1 minute. The attributes of the record are set to the properties of the FileInfo class. Click on a file name in the list box to show the XML attributes associated with the record in the right text box. The lower list shows a diagnostic log of all actions and incoming events. Double-click to clear the list.

On the Search tab, enter an XML search criteria fragment. The source code and the demo include a file Search Criteria XML Examples.txt which contains some convenient XML search criteria fragments for you to copy and paste into the sample application. Click the Search button to issue the search.

private void Search_Click(object sender, System.EventArgs e)
{
  if (textBox4.Text.Trim() == string.Empty) return;

  // create the search object and set the criteria
  PeerSearch search = graph.CreateSearch();
  search.Xml = textBox4.Text;

  try
  {
    PeerRecordCollection records = search.Search();
    listBox3.Items.Clear();
    propertyGrid1.SelectedObject = null;

    // enumerate the records and add to the list
    foreach (PeerRecord record in records)
    {
      listBox3.Items.Add(new FileItem(record.DataAsString, record.ID));
    }
  }
  catch (Exception ex)
  {
    System.Windows.Forms.MessageBox.Show(ex.Message, "Search Failed");
  }
}

If an error occurs, a popup dialog shows the error code. Otherwise, the bottom-left list is populated with the names of the files that match the search criteria. Click on this list to show the properties of the record in a property grid.

Points of Interest

In typical P2P file sharing applications, shared files are a local resource. Information about which files you are sharing is not broadcast to every peer in the network. As a result, only through remote search requests can another peer determine if you have content that matches the search criteria. The benefit of this approach is that it scales well.

In contrast, the sample application publishes information about shared files to every peer in the graph. On startup, each peer synchronizes with the graph. Once the synchronization is complete, each peer has a complete copy of all the records in the graph. As a result, searching only needs to be performed locally. However, this approach does not scale well.

A better approach would be to have two graphs. The first graph is local to the computer. No other peer synchronizes with it, but it does contain all the files to be shared. The second graph connects to the Internet, and peers use this graph to publish search requests with a very short expiration time. On receiving this request, a peer performs the search against the local graph. Any results are returned via a direct connection back to the originator.

Links to Resources

I have found the following resources to be very useful in understanding peer graphs:

Conclusion

I hope you have found this article useful. While this article provides a basic class for searching, it's possible to provide your own. A better search class could be created that bypasses the built-in search mechanism in favor of enumerating the records directly (which is faster). This better search class could provide a richer SQL-like expression engine that supports IN, NOT, CASE, BETWEEN, LIKE, type conversions and functions (NOW, LEN, SUBSTRING, ISDATE, etc.).

The next article focuses on a helper class that allows you to express a search criteria in a more SQL like manner, for example:

peercreatorid LIKE 'James *' OR (peerlastmodificationtime > 
         CAST('2003-01-31' as date) AND peerlastmodificationtime < 
         CAST('2003-02-28' as date))

instead of:

<or>
  <clause attrib="peercreatorid" 
            type="string" 
            compare="equal">James *</clause>
  <and>
    <clause attrib="peerlastmodificationtime" 
            type="date" 
            compare="greater">2003-01-31</clause>
    <clause attrib="peerlastmodificationtime" 
            type="date" 
            compare="less">2003-02-28</clause>
  </and>
</or>

The helper class provides methods to convert from the SQL-like search expression to the required XML search criteria format, and vise versa.

Stay tuned for more articles on the following topics:

  1. Peer Name Resolution - Windows Vista Enhancements
  1. Peer Graph - Search Criteria Helper
  2. Peer Graph - Importing and Exporting a Database
  1. Peer Groups and Identity
  1. Peer Collaboration - People Near Me
  2. Peer Collaboration - EndPoints
  3. Peer Collaboration - Capabilities
  4. Peer Collaboration - Presence
  5. Peer Collaboration - Invitations

If you have suggestions for other topics, please leave a comment.

History

  • Initial revision.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Adrian_Moore
Web Developer
Canada Canada
Adrian Moore is the Development Manager for the SCADA Vision system developed by ABB Inc in Calgary, Alberta.
 
He has been interested in compilers, parsers, real-time database systems and peer-to-peer solutions since the early 90's. In his spare time, he is currently working on a SQL parser for querying .NET DataSets (http://www.queryadataset.com).
 
Adrian is a Microsoft MVP for Windows Networking.

Comments and Discussions

 
GeneralProblem with few of your demos Pinmembergafferuk13-Jan-06 3:14 
GeneralRe: Problem with few of your demos PinmemberAdrian_Moore14-Jan-06 3:24 
GeneralRe: Problem with few of your demos Pinmembergafferuk14-Jan-06 10:18 
GeneralRe: Problem with few of your demos Pinmembergafferuk14-Jan-06 10:45 
GeneralRe: Problem with few of your demos PinmemberAdrian_Moore15-Jan-06 3:29 
The only time I've seen this is when you are trying to open a graph that has not been created yet. Create the graph, once and only once, then always use open to connect to it.
QuestionHaving records not sent to every peer? Pinmembergafferuk13-Jan-06 2:48 
AnswerRe: Having records not sent to every peer? PinmemberAdrian_Moore14-Jan-06 3:14 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140916.1 | Last Updated 6 Jan 2006
Article Copyright 2006 by Adrian_Moore
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid