如何存储/格式化这些无限的数据筛选条件


How to store/format these infinite data filter conditions

我正在开发一个应用程序,该应用程序将查找电子邮件收件箱并将特定电子邮件保存为其功能的一部分。发生的情况是建立 imap 连接,然后检索所有设置为"未见过"的电子邮件。每封电子邮件都根据预定义的条件进行检查,然后在满足条件时保存到数据库中。这些条件可以由用户设置,可以是以下内容:

主题

  • 包含字符串
  • 不包含字符串

身体

  • 包含字符串
  • 不包含字符串

  • 具体地址

条件可以"链接",例如:

FILTER WHERE  
Subject CONTAINS "Order Confirmation"
AND
Email FROM "billyjones26@gmail.com" OR "billyjones26@googlemail.com"

我在思考如何格式化和存储这些条件时遇到了麻烦。我需要有一个 GUI,用户可以在其中创建这些条件。

我的问题是我应该如何存储这些条件?即某种数据库表结构,或者可能转换为字符串格式并存储在单个表中。对于无限数量的用户,需要有无限数量的条件,我需要知道有哪些运营商,等等。

希望这是足够的信息!

*

为迈克尔编辑 *

所以我可以创造条件并保存它们。现在我正在检索它们并尝试匹配电子邮件。我创建了一个带有一个条件的过滤器:主题包含"TEST"。只有一封电子邮件应该与此匹配,但不知何故,所有电子邮件都被添加到匹配的数组中。

我的控制器正在接收电子邮件/条件:

public function check_email(){
    $filters = $this->filters_model->get_filters($owner_id=1);
    foreach($filters->result() as $filter){
        $emails = $this->gmail->get_emails($mailbox_id = $filter->mailbox_id, $limit = 10);
        $matched = array();
        $conditions = unserialize($filter->conditions);
        foreach($emails as $email){
            if($conditions->isMet($email) == TRUE){
                $matched[] = $email;
            }
        }
        echo count($matched);
        echo '<pre>'.$filter->title.'<br /.';
        print_r($conditions);
        echo '</pre><br />-----';
        exit;
    }
}

键值先决条件.php

这似乎工作正常,因为stripos($subject, $this->value) !== FALSE; var_dump仅显示 1 封电子邮件的 TRUE。

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 
class Keyvalueprerequisite {
    private $key;
    private $comparator;
    private $value;
    public function __construct($key, $comparator, $value){
        $this->key = $key;
        $this->comparator = $comparator;
        $this->value = $value;
    }

    public function isMet(&$context)
    {
        switch ($this->key) {
            case "subject":
                $subject = $context["subject"];
                if ($this->comparator === "in"){
                    return stripos($subject, $this->value) !== FALSE;
                } else if ($this->comparator === "!in") {
                    return stripos($subject, $this->value) === FALSE;
                }
                return FALSE;
                break;
            case "body":
                $body = $context["body"];
                if ($this->comparator === "in") {
                    return stripos($body, $this->value) !== FALSE;
                } else if ($this->comparator === "!in") {
                    return stripos($body, $this->value) === FALSE;
                }
                return FALSE;
                break;
            case "from_address":
                $from = $context["from"];
                if ($this->comparator === "=") {
                    return $this->value === $from;
                } else if ($this->comparator === "!=") {
                    return $this->value !== $from;
                } else{
                    return false;
                }
                break;
            default:
        }
        return FALSE;
    }
}

Prerequisistegroup.php

这里可能写得不太清楚。 对于 10 封电子邮件中的每一封,var_dump $result = $result && $is_met 返回 true。

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 
class Prerequisitegroup {
    private $type;
    private $prerequisites;
    public function __construct($type = 'AND'){
        $this->type = $type;
    }
    public function add(){
        foreach(func_get_args() as $prerequisite){
          $this->prerequisites[] = $prerequisite;
        }
    }
    public function isMet(&$context)
    {
        if (empty($this->prerequisites) === FALSE) {
            $result = TRUE;
            foreach ($this->prerequisites as $prerequisite) {
                $is_met = $prerequisite->isMet($context);
                if ($this->type === 'AND') {
                    $result = $result && $is_met;
                    if ($result === FALSE) {
                        return FALSE;
                    }
                } else {
                    $result = $result || $is_met;
                    if ($result === TRUE) {
                        return TRUE;
                    }
                }
            }
            return $result;
        }
        return TRUE;
    }

}

这些是我正在检查的条件:

string(3) "AND"
  ["prerequisites":"Prerequisitegroup":private]=>
  array(1) {
    [0]=>
    object(Prerequisitegroup)#23 (2) {
      ["type":"Prerequisitegroup":private]=>
      string(2) "OR"
      ["prerequisites":"Prerequisitegroup":private]=>
      array(1) {
        [0]=>
        object(Keyvalueprerequisite)#24 (3) {
          ["key":"Keyvalueprerequisite":private]=>
          string(7) "subject"
          ["comparator":"Keyvalueprerequisite":private]=>
          string(2) "in"
          ["value":"Keyvalueprerequisite":private]=>
          string(4) "TEST"
        }
      }
    }
  }
}

对于CMS,我通过将条件(先决条件(抽象为两个类来解决类似的问题:先决条件和先决条件组(前者的子类(,然后序列化它们,或者更准确地说是对象,因为在MySQL中只有一个到BLOB中。

这样做的好处是,我可以简单地反序列化对象并调用isMet()函数,而无需担心额外条件的深度或复杂性。缺点当然是我无法对此对象进行数据库查询。但在这种特殊情况下,这不是问题,从你自己的建议来看,我认为它也不在你的建议中。

Prerequisite类(或接口(实现一种方法isMet($context)告诉您条件为真与否。您案例中的上下文将是要检查$email。例如,如果来自电子邮件与"billyjones26@gmail.com"匹配。

PrerequisiteGroup以简化的方式表示ANDOR。对条件进行分组的默认方法是

下面是创建和存储条件的示例:

$c1 = new KeyValuePrerequisite("EmailFrom", "=", "billyjones26@gmail.com");
$c2 = new KeyValuePrerequisite("EmailFrom", "=", "billyjones26@googlemail.com");
$c3 = new KeyValuePrerequisite("Subject", "in", "Order Confirmation");
$from_condition = new PrerequisiteGroup('or');
$from_condition->add($c1, $c2);
$final_condition = new PrerequisiteGroup($c3, $from_condition); // defaults to and
$db->query("INSERT INTO conditions SET user_id = %i, conditions = %l", $user_id, serialize($final_condition));

下面是一个用法示例:

// Fetch conditions
$email_conditions = $user->getEmailConditions()
// Connect to your inbox and fetch relevant emails
$matching_emails = array();
foreach ($emails as $email) {
    if ($email_conditions->isMet($email) {
        $matching_emails[] = $email;
    }
}

必备接口:

interface Prerequisite 
{
    /**
     * Must return TRUE or FALSE.
     */
    public function isMet(&$context);
}

KeyValuePrerequisite 实现(在我的实现中,这实际上是一个抽象类,但出于您的目的,您可以在此处实现所有内容,您可以将其称为 EmailPrerequisite 或 EmailCondition(:

class KeyValuePrerequisite extends PrerequisiteGroup
{
    protected $key;
    protected $comparator;
    protected $value;
    public function __construct($key, $comparator, $value = NULL)
    {
        $this->key        = $key;
        $this->comparator = $comparator;
        $this->value      = $value;
    }
    public function isMet(&$context)
    {
        switch ($this->key) {
            case "Subject":
                $subject = $context["subject"];
                if ($this->comparator === "in") }
                    return stripos($subject, $this->value) !== FALSE;
                } else if ($this->comparator === "not int") {
                    return stripos($subject, $this->value) === FALSE;
                }
                return FALSE;
                break;
            case "EmailFrom":
                $from = $context["from"];
                return $this->value === $from;
                break;
            default:
        }
        return FALSE;
    }
}

先决条件组实现:

class PrerequisiteGroup implements Prerequisite
{
    private $type;
    private $prerequisites;
    public function __construct($type = 'AND')
    {
        $this->type = $type;
    }
    public function add(Prerequisite $prerequisite)
    {
        if ($prerequisite instanceof Prerequisite) {
            $this->prerequisites[] = $prerequisite;
        } else {
            throw new Exception('Unknown Prerequisite type ' . get_class($prerequisite));
        }
    }
    public function isMet(&$context)
    {
        if (empty($this->prerequisites) === FALSE) {
            $result = $this->type === 'AND';
            foreach ($this->prerequisites as $prerequisite) {
                $is_met = $prerequisite->isMet($context);
                if ($this->type === 'AND') {
                    $result = $result && $is_met;
                    if ($result === FALSE) {
                        return FALSE;
                    }
                } else {
                    $result = $result || $is_met;
                    if ($result === TRUE) {
                        return TRUE;
                    }
                }
            }
            return $result;
        }
        return TRUE;
    }
}

图形用户界面

当涉及到GUI时,有几种方法/模式。我认为,对于与电子邮件相关的应用程序,最常见的是具有许多行 AND 表达式和一个 [+] 以添加更多条件:

Row 1: [select: subject] [select:contains] [input:"Order Confirmation"]

当涉及到from时,我们需要表达OR。仅使用行很难做到这一点。这就是先决条件或条件组的概念变得方便的地方。您只需添加一个新行,该行是一个组,并让用户选择使用 AND 或 OR。

外部组是 AND 组。

因此,GUI元素相当容易转换为代码,反之亦然 - 特别是使用Prerequisite的KeyValuePrerequisite

希望这有助于或至少激发一些想法来解决您的问题。