How to match the routing key with binding pattern for RabbitMQ topic exchange using python regex?

Question:

I am basically working on RabbitMQ. I am writing a python code wherein I am trying to see if the routing key matches with the binding pattern in case of topic exchange. I came across this link- https://www.rabbitmq.com/tutorials/tutorial-five-java.html where it says- “However there are two important special cases for binding keys:

* (star) can substitute for exactly one word.

# (hash) can substitute for zero or more words.

So how do I match the routing key of message with binding pattern of queue? For example routing key of message is “my.routing.key” and the queue is bound to topic exchange with binding pattern – “my.#.*”. In general, how do I match these string patterns for topic exchange, preferably I am looking to use python regex.

Asked By: shweta

||

Answers:

This is an almost direct port of the node lib amqp-match:

import re

def amqp_match(key: str, pattern: str) -> bool:
    if key == pattern:
        return True
    replaced = pattern.replace(r'*', r'([^.]+)').replace(r'#', r'([^.]+.?)+')
    regex_string = f"^{replaced}$"
    match = re.search(regex_string, key)
    return match is not None

I have some Java code if that can help you

Pattern toRegex(String pattern) {
    final String word = "[a-z]+";

    // replace duplicate # (this makes things simpler)
    pattern = pattern.replaceAll("#(?:\.#)+", "#");

    // replace *
    pattern = pattern.replaceAll("\*", word);

    // replace #

    // lone #
    if ("#".equals(pattern)) return Pattern.compile("(?:" + word + "(?:\." + word + ")*)?");

    pattern = pattern.replaceFirst("^#\.", "(?:" + word + "\.)*");
    pattern = pattern.replaceFirst("\.#", "(?:\." + word + ")*");

    // escape dots that aren't escapes already
    pattern = pattern.replaceAll("(?<!\\)\.", "\\.");

    return Pattern.compile("^" + pattern + "$");
}

maybe someone can translate that into python.

Answered By: benez

We’re using this pattern to convert RabbitMQ patterns to regexes:

from typing import Pattern

def convert(pattern: str) -> Pattern:
    pattern = (
        pattern
        .replace('*', r'([^.]+)')
        .replace('.#', r'(.[^.]+)*')
        .replace('#.', r'([^.]+.)*')
    )
    return re.compile(f"^{pattern}$")
Answered By: Josh Bode

I am using this java function:

public boolean matchRoutingPatterns(String routingTopic, String routingKey)
{
    // replace duplicates
    routingTopic = routingTopic.replaceAll("#(?:\.#)+", "#").replaceAll("([#*])\1{2,}", "$1");
    routingKey   = routingKey.replaceAll("#(?:\.#)+", "#").replaceAll("([#*])\1{2,}", "$1");

    String[] routingTopicPath = Strings.splitList( routingTopic.replace('.', ','));
    String[] routingKeyPath   = Strings.splitList( routingKey.replace('.', ','));

    int i=0;
    int j=0;
    while (i<routingTopicPath.length && j<routingKeyPath.length)
    {
        if ("#".equals(routingTopicPath[i]) || "#".equals(routingKeyPath[j]))
        {
            if (routingTopicPath.length-i > routingKeyPath.length-j)
                { i++; }
            else
            if (routingTopicPath.length-i < routingKeyPath.length-j)
                { j++; }
            else
                { i++; j++; }
        }
        else
        if ("*".equals(routingTopicPath[i]) || "*".equals(routingKeyPath[j]))
        {
            i++; j++;
        }
        else
        if (routingTopicPath[i].equals(routingKeyPath[j]))
        {
            i++; j++;
        }
        else
        {
            return false;
        }
    }

    return true;
}
Answered By: user19969784
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.