MQTT 协议的订阅的话题名称采用修改版UTF-8编码,订阅者的话题支持通配符#和+

用户需要一次订阅多个具有类似结构的话题,可以在话题过滤器中包含通配符。通配符只可用在话题过滤器中,在发布应用消息时的话题名不允许包含通配符,话题通配符有两种:

  • #:表示匹配>=0个层次,比如a/#就匹配a/b,a/b/c(不能匹配a/,后面必须有其它话题)。单独的一个#表示匹配所有,不允许a#或a/#/c等形式。

  • +:表示匹配一个层次,例如a/+匹配a/b,a/c,不匹配a/b/c。单独的一个+是允许的,但a+为非法形式。

1. 实现一

  1. 先使用排列组合生成有可能匹配的所有话题模式,然后放置到Object中,在这里当只有唯一元素的Set使用
  2. 在目标话题中直接查找,如果在所有话题模式中包含这个目标话题,就返回真。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    "use strict";

    var LRU = require("lru-cache");

    var logger = require('./logHelper').logger(__filename)

    var cache = LRU({
    max: 10000,
    maxAge: 1000 * 60 * 60
    });

    /**
    * Generate the possible patterns that might match a topic.
    *
    * @param {String} the topic
    * @return the object of the patterns
    */
    function _topicPatterns(topic) {
    var parts = topic.split("/");
    var patterns = {};
    patterns[topic] = true
    var i, a = [], b = [], j, k, h, list = [];

    for (j = 1; j < parts.length; j++) {
    list.length = 0; // clear the array

    for (i = 0; i < parts.length; i++) {
    a.length = 0;
    b.length = 0;

    list.push(i);
    for (h = 1; list.length < j; h++) {
    list.unshift(parts.length - h);
    }

    for (k = 0; k < parts.length; k++) {
    if (list.indexOf(k) >= 0) {
    a.push(parts[k]);
    b.push(parts[k]);
    } else {
    if (k === 0 || a[a.length - 1] !== "#") {
    a.push("#");
    }
    b.push("+");
    }
    }

    patterns[(a.join("/"))] = true;
    patterns[(b.join("/"))] = true;
    list.shift();
    }
    }

    return patterns;
    }

    /**
    * Generate the possible patterns that might match a topic.
    * Memozied version.
    *
    * @param {String} the topic
    * @return the map of the patterns
    */
    function topicPatterns(topic) {
    var result = cache.get(topic);
    if (!result) {
    result = _topicPatterns(topic);
    }
    cache.set(topic, result);
    return result;
    }


    /**
    * 根据输入的话题匹配目标话题列表,匹配成功就返回true
    * 使用lur缓存,现在有BUG,只能匹配/开头的话题
    *
    * @param {String} 输入的话题
    * @param {list} 目标话题
    */
    function match(topic, targetTopics) {
    var possibles = topicPatterns(topic);
    logger.debug('possible topic patterns: ')
    logger.debug(possibles)
    for (var i = 0; i < targetTopics.length; i++) {
    var targetTopic = targetTopics[i];
    if (possibles[targetTopic]) {
    logger.debug('Topic match: %s', true)
    return true;
    }
    }
    logger.debug('Topic match: %s', false)
    return false;
    }

    module.exports.match = match;

    module.exports.topicPatterns = topicPatterns
    测试
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    function testMatch() {

    var subscribeTopics = ['/a/sdfsdfds/u/sdfsdf/#']

    var publishTopic = '/a1/sdfsdfds/u/sdfsdf/12222'

    var result = match(publishTopic, subscribeTopics)

    console.log(result==false)

    var result = match('/a/sdfsdfds/u/sdfsdf/12222', subscribeTopics)

    console.log(result==false)
    }
    输出
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127

    [2017-03-05 11:31:40.157] [DEBUG] file - possible topic patterns:
    [2017-03-05 11:31:40.158] [DEBUG] file - { '/a1/sdfsdfds/u/sdfsdf/12222': true,
    '/#': true,
    '/+/+/+/+/+': true,
    '#/a1/#': true,
    '+/a1/+/+/+/+': true,
    '#/sdfsdfds/#': true,
    '+/+/sdfsdfds/+/+/+': true,
    '#/u/#': true,
    '+/+/+/u/+/+': true,
    '#/sdfsdf/#': true,
    '+/+/+/+/sdfsdf/+': true,
    '#/12222': true,
    '+/+/+/+/+/12222': true,
    '/#/12222': true,
    '/+/+/+/+/12222': true,
    '/a1/#': true,
    '/a1/+/+/+/+': true,
    '#/a1/sdfsdfds/#': true,
    '+/a1/sdfsdfds/+/+/+': true,
    '#/sdfsdfds/u/#': true,
    '+/+/sdfsdfds/u/+/+': true,
    '#/u/sdfsdf/#': true,
    '+/+/+/u/sdfsdf/+': true,
    '#/sdfsdf/12222': true,
    '+/+/+/+/sdfsdf/12222': true,
    '/#/sdfsdf/12222': true,
    '/+/+/+/sdfsdf/12222': true,
    '/a1/#/12222': true,
    '/a1/+/+/+/12222': true,
    '/a1/sdfsdfds/#': true,
    '/a1/sdfsdfds/+/+/+': true,
    '#/a1/sdfsdfds/u/#': true,
    '+/a1/sdfsdfds/u/+/+': true,
    '#/sdfsdfds/u/sdfsdf/#': true,
    '+/+/sdfsdfds/u/sdfsdf/+': true,
    '#/u/sdfsdf/12222': true,
    '+/+/+/u/sdfsdf/12222': true,
    '/#/u/sdfsdf/12222': true,
    '/+/+/u/sdfsdf/12222': true,
    '/a1/#/sdfsdf/12222': true,
    '/a1/+/+/sdfsdf/12222': true,
    '/a1/sdfsdfds/#/12222': true,
    '/a1/sdfsdfds/+/+/12222': true,
    '/a1/sdfsdfds/u/#': true,
    '/a1/sdfsdfds/u/+/+': true,
    '#/a1/sdfsdfds/u/sdfsdf/#': true,
    '+/a1/sdfsdfds/u/sdfsdf/+': true,
    '#/sdfsdfds/u/sdfsdf/12222': true,
    '+/+/sdfsdfds/u/sdfsdf/12222': true,
    '/#/sdfsdfds/u/sdfsdf/12222': true,
    '/+/sdfsdfds/u/sdfsdf/12222': true,
    '/a1/#/u/sdfsdf/12222': true,
    '/a1/+/u/sdfsdf/12222': true,
    '/a1/sdfsdfds/#/sdfsdf/12222': true,
    '/a1/sdfsdfds/+/sdfsdf/12222': true,
    '/a1/sdfsdfds/u/#/12222': true,
    '/a1/sdfsdfds/u/+/12222': true,
    '/a1/sdfsdfds/u/sdfsdf/#': true,
    '/a1/sdfsdfds/u/sdfsdf/+': true,
    '#/a1/sdfsdfds/u/sdfsdf/12222': true,
    '+/a1/sdfsdfds/u/sdfsdf/12222': true }
    [2017-03-05 11:31:40.161] [DEBUG] file - Topic match: false
    [2017-03-05 11:33:18.170] [DEBUG] file - possible topic patterns:
    [2017-03-05 11:33:18.173] [DEBUG] file - { '/a/sdfsdfds/u/sdfsdf/12222': true,
    '/#': true,
    '/+/+/+/+/+': true,
    '#/a/#': true,
    '+/a/+/+/+/+': true,
    '#/sdfsdfds/#': true,
    '+/+/sdfsdfds/+/+/+': true,
    '#/u/#': true,
    '+/+/+/u/+/+': true,
    '#/sdfsdf/#': true,
    '+/+/+/+/sdfsdf/+': true,
    '#/12222': true,
    '+/+/+/+/+/12222': true,
    '/#/12222': true,
    '/+/+/+/+/12222': true,
    '/a/#': true,
    '/a/+/+/+/+': true,
    '#/a/sdfsdfds/#': true,
    '+/a/sdfsdfds/+/+/+': true,
    '#/sdfsdfds/u/#': true,
    '+/+/sdfsdfds/u/+/+': true,
    '#/u/sdfsdf/#': true,
    '+/+/+/u/sdfsdf/+': true,
    '#/sdfsdf/12222': true,
    '+/+/+/+/sdfsdf/12222': true,
    '/#/sdfsdf/12222': true,
    '/+/+/+/sdfsdf/12222': true,
    '/a/#/12222': true,
    '/a/+/+/+/12222': true,
    '/a/sdfsdfds/#': true,
    '/a/sdfsdfds/+/+/+': true,
    '#/a/sdfsdfds/u/#': true,
    '+/a/sdfsdfds/u/+/+': true,
    '#/sdfsdfds/u/sdfsdf/#': true,
    '+/+/sdfsdfds/u/sdfsdf/+': true,
    '#/u/sdfsdf/12222': true,
    '+/+/+/u/sdfsdf/12222': true,
    '/#/u/sdfsdf/12222': true,
    '/+/+/u/sdfsdf/12222': true,
    '/a/#/sdfsdf/12222': true,
    '/a/+/+/sdfsdf/12222': true,
    '/a/sdfsdfds/#/12222': true,
    '/a/sdfsdfds/+/+/12222': true,
    '/a/sdfsdfds/u/#': true,
    '/a/sdfsdfds/u/+/+': true,
    '#/a/sdfsdfds/u/sdfsdf/#': true,
    '+/a/sdfsdfds/u/sdfsdf/+': true,
    '#/sdfsdfds/u/sdfsdf/12222': true,
    '+/+/sdfsdfds/u/sdfsdf/12222': true,
    '/#/sdfsdfds/u/sdfsdf/12222': true,
    '/+/sdfsdfds/u/sdfsdf/12222': true,
    '/a/#/u/sdfsdf/12222': true,
    '/a/+/u/sdfsdf/12222': true,
    '/a/sdfsdfds/#/sdfsdf/12222': true,
    '/a/sdfsdfds/+/sdfsdf/12222': true,
    '/a/sdfsdfds/u/#/12222': true,
    '/a/sdfsdfds/u/+/12222': true,
    '/a/sdfsdfds/u/sdfsdf/#': true,
    '/a/sdfsdfds/u/sdfsdf/+': true,
    '#/a/sdfsdfds/u/sdfsdf/12222': true,
    '+/a/sdfsdfds/u/sdfsdf/12222': true }
    [2017-03-05 11:33:18.179] [DEBUG] file - Topic match: true

    实现二

    把话题路径中的#+替换成语言内置的正则语法,然后进行匹配
    1
    2
    3
    4
    5
    6
    7
    var matchTopic = function(inputTopic, targetTopic) {
    if (inputTopic== "#") {
    return true;
    }
    var re = new RegExp("^"+inputTopic.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$");
    return re.test(targetTopic);
    };