douyun3799 2012-08-27 11:56
浏览 7
已采纳

如何存储/格式化这些无限数据过滤条件

I'm developing an app that will look up an email inbox and save specific emails as part of it's functionality. What happens is an imap connection is made and then all emails that are set as UNSEEN are retrieved. Each email is checked against pre-defined conditions and then saved to a DB if they are met. These conditions can be set by the user and can be the following:

Subject

  • Contains string
  • Does not contain string

Body

  • Contains string
  • Does not contain string

From

  • Specific address

Conditions can be "chained" for instance:

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

I'm having trouble thinking of how to format and store these conditions. I need to have a GUI where the user can create these conditions.

My questions is how should I store these conditions? I.e some sort of DB table structure, or maybe converted into a string format and stored in a single table. There needs to be a unlimited number of conditions for an unlimited number of users, and I need to know what operators there are, etc.

Hope that's enough information!

* EDIT FOR MICHAEL *

So I can create the conditions and save them. Now I'm retrieving them and trying to match emails. I created a filter with one condition: Subject contains 'TEST'. Only one email should match this but somehow all emails are being added to the matched array.

My controller that is getting the emails/conditions:

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;
    }
}

Keyvalueprerequisite.php

This seems to work fine as a var_dump of stripos($subject, $this->value) !== FALSE; shows only TRUE for 1 email.

<?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

Something might not be quite write here. a var_dump of $result = $result && $is_met returns true for each of the 10 emails.

<?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;
    }


}

These are my conditions that are being checked:

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"
        }
      }
    }
  }
}
  • 写回答

1条回答 默认 最新

  • duangao8359 2012-08-27 14:40
    关注

    For a CMS I solved a similar problem, by abstracting the conditions (Prerequisites) into two classes: Prerequisite and PrerequisiteGroup (a subclass of the former, and then serialized them, or more precisely the object, since there would only be one into a BLOB in MySQL.

    The advantage of this was that I could simply deserialize the object and invoke an isMet() function without bothering about the depth or complexity of the perquisites. The disadvantage was of course that I couldn't make DB queries on this objects. But that wasn't a problem in this particular case, and from your own suggestions I think it isn't in yours as well.

    The Prerequisite class (or interface) implements one method isMet($context) that tells you where your condition is true or not. The context in your case would be the $email to be examined. E.g. if from email matches "billyjones26@gmail.com".

    The PrerequisiteGroup represents AND and OR, in a simplified way. The default way to group conditions is by

    Here is an example of creating and storing the conditions:

    $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));
    

    Here is an example usage:

    // 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;
        }
    }
    

    Prerequisite interface:

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

    KeyValuePrerequisite implementation (in my implementation this actually an abstract class but for your purpose you can implement everything here, you could call it EmailPrerequisite or 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;
        }
    }
    

    PrerequisiteGroup implementation:

    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

    When it comes to GUI there are several approaches/patterns. The most common, I think, for email related apps is to have a number of rows of expressions that are ANDed and a [+] to add more conditions:

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

    When it comes to the from we need to express OR. This is hard to do with just rows. This is where the concept of a Prerequisite or Condition group becomes handy. You simply add a new row which is a group and let the user to choose to use AND or OR.

    The outer group is an AND group.

    So the GUI elements are fairly easy to transform into code and vice versa - especially with the KeyValuePrerequisite impl. of Prerequisite.

    Hope this helps or at least sparked some ideas to solve your problem.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥30 这是哪个作者做的宝宝起名网站
  • ¥60 版本过低apk如何修改可以兼容新的安卓系统
  • ¥25 由IPR导致的DRIVER_POWER_STATE_FAILURE蓝屏
  • ¥50 有数据,怎么建立模型求影响全要素生产率的因素
  • ¥50 有数据,怎么用matlab求全要素生产率
  • ¥15 TI的insta-spin例程
  • ¥15 完成下列问题完成下列问题
  • ¥15 C#算法问题, 不知道怎么处理这个数据的转换
  • ¥15 YoloV5 第三方库的版本对照问题
  • ¥15 请完成下列相关问题!