我有一组数据和一组我想针对该数据运行的搜索过滤器。过滤器遵循LDAP搜索过滤器格式,并被解析为表达式树。数据一次读取一个项目并通过所有过滤器处理。中间匹配结果存储在树的每个叶节点中,直到所有数据都已处理完毕。然后通过遍历树并将逻辑运算符应用于每个叶节点的中间结果来获得最终结果。例如,如果我有过滤器(&(a=b)(c=d))
,那么我的树将如下所示:
root = "&"
left = "a=b"
right = "c=d"
因此,如果a=b
和c=d
,则左右子节点都匹配,因此过滤器匹配。
数据是不同类型对象的集合,每个对象都有自己的字段。例如,假设集合代表学校的一个班级:
class { name = "math" room = "12A" }
teacher { name = "John" age = "35" }
student { name = "Billy" age = "6" grade = "A" }
student { name = "Jane" age = "7" grade = "B" }
因此,过滤器可能看起来像(&(teacher.name=John)(student.age>6)(student.grade=A))
,并且可以这样解析:
root = "&"
left = "teacher.name=John"
right = "&"
left = "student.age>6"
right = "student.grade=A"
我对它运行class
对象;无匹配。我对它运行teacher
对象; root.left
是匹配的。我对它运行第一个student
节点; root.right.right
是匹配的。我对它运行第二个student
节点; root.right.left
是匹配的。然后我遍历树并确定所有节点匹配,因此最终结果是匹配。
问题是中间匹配需要基于共性来约束:student.age
和student.grade
过滤器需要以某种方式绑定在一起,以便只有当它们匹配时才存储中间匹配宾语。我不能为我的生活弄清楚如何做到这一点。
我的过滤器节点抽象基类:
class FilterNode
{
public:
virtual void Evaluate(string ObjectName, map<string, string> Attributes) = 0;
virtual bool IsMatch() = 0;
};
我有一个LogicalFilterNode
类来处理逻辑AND,OR和NOT操作;它的实现非常简单:
void LogicalFilterNode::Evaluate(string ObjectName, map<string, string> Attributes)
{
m_Left->Evaluate(ObjectName, Attributes);
m_Right->Evaluate(ObjectName, Attributes);
}
bool LogicalFilterNode::IsMatch()
{
switch(m_Operator)
{
case AND:
return m_Left->IsMatch() && m_Right->IsMatch();
case OR:
return m_Left->IsMatch() || m_Right->IsMatch();
case NOT:
return !m_Left->IsMatch();
}
return false;
}
然后我有一个处理叶节点的ComparisonFilterNode
类:
void ComparisonFilterNode::Evaluate(string ObjectName, map<string, string> Attributes)
{
if(ObjectName == m_ObjectName) // e.g. "teacher", "student", etc.
{
foreach(string_pair Attribute in Attributes)
{
Evaluate(Attribute.Name, Attribute.Value);
}
}
}
void ComparisonFilterNode::Evaluate(string AttributeName, string AttributeValue)
{
if(AttributeName == m_AttributeName) // e.g. "age", "grade", etc.
{
if(Compare(AttributeValue, m_AttributeValue) // e.g. "6", "A", etc.
{
m_IsMatch = true;
}
}
}
bool ComparisonFilterNode::IsMatch() { return m_IsMatch; }
如何使用:
FilterNode* Root = Parse(...);
foreach(Object item in Data)
{
Root->Evaluate(item.Name, item.Attributes);
}
bool Match = Root->IsMatch();
基本上我需要的是AND语句,其中子项具有相同的对象名称,AND语句应仅在子项匹配同一对象时匹配。
答案 0 :(得分:1)
创建一个新的一元“运算符”,我们称之为thereExists
,其中:
具体来说,对于表达式树中thereExists
运算符的每个实例,您应该存储一个位,指示到目前为止看到的任何输入记录是否满足此树节点下的子表达式。这些标志最初将设置为false
。
要继续有效地处理数据集(即通过输入记录输入记录,而不必将整个数据集加载到内存中),您应首先预处理查询表达式树以提取{{1}的所有实例的列表运算符。然后,当您在每个输入记录中读取时,针对仍然将thereExists
标志设置为satisfied
的每个运算符的子子表达式对其进行测试。现在满足的任何子表达式都应该将其父false
节点的thereExists
标志切换为satisfied
- 并且最好还将满意记录的副本附加到新的...满意的true
节点,如果您想要查看整个查询的“是”或“否”答案。
在如上所述处理完所有输入记录之后,您只需要评估一次thereExists
节点上方的树节点。请注意,引用单个记录的属性的任何内容必须出现在树中thereExists
节点下的某处。树中thereExists
节点以上的所有内容只允许测试集合的“全局”属性,或者使用逻辑运算符(AND,OR,XOR,NOT等)组合thereExists
个节点的结果。 )。逻辑运算符本身可以出现在树中的任何位置。
使用此功能,您现在可以评估
等表达式thereExists
如果记录集合同时包含名称为“John”的教师和名为“Billy”的学生或年龄超过6岁的A学生,则会报告“是”,否则为“否”。如果您按照我的建议跟踪令人满意的记录,那么在“是”答案的情况下,您也可以将这些记录转出。
您还可以添加第二个运算符类型root = "&"
left = thereExists
child = "teacher.name=John"
right = "|"
left = thereExists
child = "&"
left = "student.age>6"
right = "student.grade=A"
right = thereExists
child = "student.name = Billy"
,它会检查每个输入记录的子表达式是否为真。但这可能没那么有用,无论如何你可以用forAll
来模拟forAll(expr)
。