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.
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.
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}$")
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;
}
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.
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.
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}$")
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;
}