Peer Graph - 搜索标准帮助程序






4.17/5 (2投票s)
2006年1月16日
7分钟阅读

37040

510
使用Microsoft的Peer-to-Peer技术将表达式转换为Peer Graph的XML搜索条件。
背景
Microsoft的Peer-to-Peer Graphing技术为Windows的点对点应用程序提供了稳定、可靠和健壮的通信基础设施。对等节点使用Peer Name Resolution Protocol (PNRP - 一个无服务器DNS) 在图中注册和发现其他对等节点。图是连接对等节点、服务和资源的基础。对等节点可以是用户交互式应用程序、服务或资源。Graphing允许数据在对等节点之间高效可靠地传递。
Microsoft的整个Peer-to-Peer技术通过最新的Platform SDK暴露为C/C++ API调用。然而,本文中的代码展示了如何使用C#中的.NET托管代码来调用这些API。
引言
本文介绍了一个辅助类,该类允许您以更类似SQL的方式表达搜索条件。正如我们在上一篇文章中学到的,搜索对等节点记录中的元数据使用简单的XML搜索条件。例如
<peersearch>
<and>
<and>
<clause attrib="creationtime" type="date"
compare="greaterorequal">2005-07-01T00:00:00.0000000-06:00
</clause>
<clause attrib="creationtime" type="date"
compare="lessorequal">2005-09-01T00:00:00.0000000-06:00
</clause>
</and>
<clause attrib="length" type="int"
compare="greater">15000</clause>
</and>
</peersearch>
本文介绍的辅助类提供了一个方法,用于将类似SQL的表达式转换为所需的XML搜索条件格式。以下表达式转换为上面的XML片段
creationtime between '2005-07-01' and '2005-09-01' and length > 15000
可以说,这样更容易阅读。
语法
首先,我使用GOLD (Grammar Oriented Language Development) 工具包来表示我的语法。如果您想更好地理解这项技术,我建议您阅读几个月前我写的关于GOLD的入门文章。
我创建了一个简单的语法来匹配搜索条件的功能。让我们从表示与XML搜索条件类似的表达式所需的规则开始。
<Search List> ::= <Search List> AND <Comparison>
| <Search List> OR <Comparison>
| <Comparison>
搜索包含比较。它可以是单个比较,如a=1,也可以是多个由AND或OR分隔的比较。
<Comparison> ::= Identifier LIKE StringLiteral
| Identifier BETWEEN <Value> AND <Value>
| Identifier IN '(' <Value List> ')'
| Identifier '>' <Value>
| Identifier '<' <Value>
| Identifier '<=' <Value>
| Identifier '>=' <Value>
| Identifier '=' <Value> !Equal
| Identifier '<>' <Value> !Not equal
| Identifier '!=' <Value> !Not equal
| '(' <Search List> ')'
有几种类型的比较。
- LIKE将值与字符串模式进行比较,例如,name LIKE 'James *'。
- BETWEEN确定值是否在范围内,例如,created BETWEEN '2005-07-01' and '2005-08-01'。
- IN将值与一组字面值进行比较,例如,grade IN ('A', 'B', 'C')。
- 还有一些典型的比较,如a=1, a>2, and b!=5。
- 比较也可以使用括号进行分组,以确保优先级。
接下来是值。值是字面量,如字符串或数字。数字可以是正数或负数,可以是整数或浮点数。注意科学计数法 (1.0e+3) 也得到支持。
<Unary> ::= '-' | '+' |
<Value> ::= <Unary> IntegerLiteral
| <Unary> RealLiteral
| StringLiteral
| CAST '(' <Value> AS <Type> ')'
值也可以通过CAST显式地从一种数据类型转换为另一种数据类型。请注意,日期会隐式地从字符串转换;因此,永远不需要CAST('1900-01-01' as string)。
最后,值得一提的是,BETWEEN、IN、CAST、LIKE、AND和OR这些标记*不*区分大小写。
PeerSearchCriteria类
此类实现了两个共享(静态)方法,称为ExpressionToXML
。第一个方法接受两个参数;要解析的表达式和一个布尔值,指示返回的XML是否应该格式化。第二个方法只接受表达式参数,并返回未格式化的XML。
Public Shared Function ExpressionToXML(ByVal _
Expression As String, ByVal Indent As Boolean) As String
If (Expression.Trim = String.Empty) Then
Return "<peersearch/>"
If _grammar Is Nothing Then
Dim exec As [Assembly] = [Assembly].GetExecutingAssembly
Dim stream As stream = _
exec.GetManifestResourceStream("RecordSearch.cgt")
_grammar = New Grammar(New BinaryReader(stream))
End If
Dim parser As SearchCriteriaParser = _
New SearchCriteriaParser(Expression, _grammar)
If parser.DoParse(New StringReader(Expression)) = _
ParseMessage.Accept Then
Dim xw As New System.Xml.XmlTextWriter(New MemoryStream, _
System.Text.Encoding.Unicode)
If Indent Then xw.Formatting = Xml.Formatting.Indented
xw.WriteStartElement("peersearch")
CType(parser.CurrentReduction.Tag, _
IASTNode).WriteToXML(xw)
xw.WriteEndElement()
xw.Flush()
xw.BaseStream.Position = 0
ExpressionToXML = New StreamReader(xw.BaseStream).ReadToEnd()
Else
Throw New SearchCritieraException(parser.Message)
End If
End Function
Public Shared Function ExpressionToXML(ByVal _
Expression As String) As String
Return ExpressionToXML(Expression, False)
End Function
ExpressionToXML
方法首先检查空表达式字符串。然后,如果这是该方法第一次被调用,它将使用嵌入资源中的编译后的二进制语法版本来初始化规则引擎。然后创建解析器并解析表达式。这会创建一个抽象语法树 (AST)。AST是一个对象树,其中每个对象(节点)代表语法中规则的标记。如果发生任何错误,将抛出一个SearchCriteriaException
。否则,将创建一个XmlTextWriter
,并从根节点开始遍历AST。AST中的每个节点都贡献其代表规则的XML描述。遍历完整个树后,将构建完整的XML片段。XML被提取并作为字符串返回。
AST中存储三种类型的节点;搜索列表、比较和值。搜索列表和比较节点实现了IASTNode
接口,该接口有一个生成节点代表的规则的等效XML片段的单一方法。例如,下面的SearchList
类代表了语法中的<Search List>
规则。
Class SearchList
Implements IASTNode
Friend List As New ArrayList '(Of SearchItem)
Public Sub New(ByVal item As SearchItem)
List.Add(item)
End Sub
Public Sub Add(ByVal item As SearchItem)
List.Add(item)
End Sub
Public Sub WriteToXML(ByVal xw As System.Xml.XmlTextWriter) _
Implements IASTNode.WriteToXML
Dim item As SearchItem = CType(List(0), SearchItem)
If List.Count = 1 Then
item.WriteToXML(xw)
Else
' operation is always the second item in the list
Dim Operation As String = CType(List(1), _
SearchItem).Operation
xw.WriteStartElement(Operation)
Dim i As Integer = 0
Do
item.WriteToXML(xw)
i += 1
item = CType(List(i), SearchItem)
Loop While i < List.Count - 1
' don't forget the last item
item.WriteToXML(xw)
xw.WriteEndElement()
End If
End Sub
End Class
搜索列表包含一个SearchItem
列表。SearchItem
是比较加上一个操作(AND、OR或空)。WriteXML
方法首先检查列表中SearchItem
的数量。如果只有一个(例如,name = 'Bill'),它会调用SearchItem
的WriteXML
方法。否则,有一个列表(例如,name = 'Bill' AND age > 50)。根据搜索记录的模式规则,必须首先生成<and>
或<or>
标记。在此标记内,WriteXML
方法循环遍历列表中的SearchItem
。这可能会导致递归,但这已被正确处理。最后,处理列表中的最后一项并写入结束标记。
比较要简单得多。所有比较都派生自一个名为Comparison
的抽象基类。
MustInherit Class Comparison
Implements IASTNode
Public Const EQUAL_COMPARE As String = "equal"
Public Const GREATER_COMPARE As String = "greater"
Public Const LESS_COMPARE As String = "less"
Public Const NOTEQUAL_COMPARE As String = "notequal"
Public Const GREATEROREQUAL_COMPARE As String = "greaterorequal"
Public Const LESSOREQUAL_COMPARE As String = "lessorequal"
Public MustOverride Sub WriteToXML(ByVal xw As _
System.Xml.XmlTextWriter) Implements IASTNode.WriteToXML
Protected Sub WriteClause(ByVal xw As System.Xml.XmlTextWriter, _
ByVal Attrib As String, ByVal Type As String, _
ByVal Compare As String, ByVal Value As String)
xw.WriteStartElement("clause")
xw.WriteAttributeString("attrib", Attrib)
xw.WriteAttributeString("type", Type)
xw.WriteAttributeString("compare", Compare)
xw.WriteString(Value)
xw.WriteEndElement()
End Sub
End Class
Comparison
类声明了一些字符串常量,用于XML比较名称。它还提供了一个有用的方法来封装生成<clause attrib="" type="" compare=""></clause>
标记。以下示例显示了BETWEEN规则的派生类。
Class BetweenComparison
Inherits Comparison
Friend Identifier As String
Friend LeftBetween As Value
Friend RightBetween As Value
Public Sub New(ByVal Expr As String, _
ByVal Left As Value, ByVal Right As Value)
Identifier = Expr
LeftBetween = Left
RightBetween = Right
End Sub
Public Overrides Sub WriteToXML(ByVal xw As System.Xml.XmlTextWriter)
xw.WriteStartElement("and")
WriteClause(xw, Identifier, LeftBetween.DataType, _
GREATEROREQUAL_COMPARE, LeftBetween.Value)
WriteClause(xw, Identifier, RightBetween.DataType, _
LESSOREQUAL_COMPARE, RightBetween.Value)
xw.WriteEndElement()
End Sub
End Class
同样,规则的信息被存储;在这种情况下,是标识符以及左值和右值。由于BETWEEN表示范围内的值,这会转换为<and>
标记内的两个比较子句。
Value
类是最简单的。所有值都派生自一个名为Value
的抽象基类。
MustInherit Class Value
Public Const INT_TYPE As String = "int"
Public Const DATE_TYPE As String = "date"
Public Const STRING_TYPE As String = "string"
Public MustOverride ReadOnly Property Value() As String
Public MustOverride ReadOnly Property DataType() As String
End Class
为XML数据类型声明了常量字符串。派生类必须覆盖方法以指示其值和数据类型。以下是代表日期的类的代码。
Class DateValue
Inherits Value
Private Data As Date
Public Sub New(ByVal v As Date)
Data = v
End Sub
Public Overrides ReadOnly Property DataType() As String
Get
Return MyBase.DATE_TYPE
End Get
End Property
Public Overrides ReadOnly Property Value() As String
Get
Return System.Xml.XmlConvert.ToString(Data)
End Get
End Property
End Class
DataType
属性返回日期数据类型的XML字符串。Value
属性返回日期的XML值。
使用示例应用程序
示例应用程序允许您首先创建一个图(未加密的对等节点名称0.SearchSharedFiles
)并设置一个初始标识。第一个实例应该使用此标识打开。它将暂停几秒钟以查找其他实例,然后开始监听。应用程序的每个后续实例都应该使用不同的标识打开图。这些实例将连接到最近的对等节点并同步。应用程序的每个实例都是一个对等节点。
在“共享”选项卡上,使用“添加文件夹”按钮选择包含文件的文件夹。选择文件夹后,文件夹中的每个文件都作为记录发布到图中。请注意,记录设置为在10分钟后过期。记录的属性被设置为FileInfo
类的属性。单击列表框中的文件名以在右侧文本框中显示与记录关联的XML属性。下面的列表显示了所有操作和传入事件的诊断日志。双击以清除列表。请注意,“windows”和“system32”文件夹提供了一组良好的文件用于搜索。
在“搜索”选项卡上,输入搜索条件表达式。源代码和演示包括一个名为Search Criteria Expression Examples.txt的文件,其中包含一些方便的搜索条件表达式供您复制并粘贴到示例应用程序中。单击“搜索”按钮以发出搜索。该表达式使用辅助类快速转换为其XML格式,并在右上角的文本框中显示。然后将XML片段传递给Search
方法。
private void Search_Click(object sender, System.EventArgs e)
{
PeerSearch search = graph.CreateSearch();
try
{
// convert expression to XML search criteria fragment
textBox5.Text =
Peer.Utility.PeerSearchCriteria.ExpressionToXML(textBox4.Text,
true);
search.Xml = textBox5.Text;
PeerRecordCollection records = search.Search();
listBox3.Items.Clear();
propertyGrid1.SelectedObject = null;
// display the results in the list box
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");
}
}
如果发生错误,将显示一个弹出对话框显示错误代码。否则,左下方的列表将填充与搜索条件匹配的文件名。单击此列表以在属性网格中显示记录的属性。
关注点
单元测试
示例代码包含一个包含40多个单元测试的项目,用于检查各种表达式是否都已正确转换。
限制
Microsoft提到底层PeerGraphSearchRecords
API的性能效率不高。对于简单的表达式,它还不错。然而,当你开始使用更复杂的表达式和多个字符串模式时,性能会迅速变得不可用。例如,以下表达式
name in ('a*','b*','c*','d*','e*')
这要么会无限期地使用100%的CPU,要么在几分钟后导致错误。由于其他错误,我尚无法确定这在Windows Vista中是否已得到修正。
资源链接
我发现以下资源对于理解对等图形非常有帮助
结论
希望您觉得本文很有用。下一篇文章将通过讨论如何导入和导出数据库来完成Graphing系列。请继续关注以下主题的更多文章
- 对等名称解析 - Windows Vista 增强功能
- Peer Graph - 导入和导出数据库
- 对等组和身份
- 对等协作 - 附近的人
- 对等协作 - 端点
- 对等协作 - 能力
- 对等协作 - 在线状态
- 对等协作 - 邀请
如果您对其他主题有建议,请留言。
历史
- 初始版本。