У меня есть набор данных и набор поисковых фильтров, которые я хочу использовать для этих данных. Фильтры соответствуют формату фильтра поиска 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
, который обрабатывает логические операции И, ИЛИ и НЕ; его реализация довольно проста:
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 должен совпадать только в том случае, если дочерние элементы соответствуют одному и тому же объекту.
teacher
, либоstudent
. Такой фильтр, какteacher.name=John AND student.grade=A
, никогда не будет соответствовать одному элементу. - person Luke   schedule 23.10.2013