LogoLogo
About
  • Home
  • Guides
  • BDK
  • REST API
  • BDK Overview
  • Planning Guide
  • Setup Guide
  • Development Guide
  • Deployment
  • Learning a BDK Book
  • Connection Commands
  • BDK API Reference
    • Concepts
    • Connections
    • Decorators
      • @book
      • @concept
      • @config
      • @connect
      • @oauth
      • @oauthtoken
      • @procedure
    • Docstrings
    • Enums
      • FilterBinaryOperator
      • FilterUnaryOperator
    • Filter Expressions
    • Noun Phrases
    • Procedures
Powered by GitBook
On this page
  • Overview
  • Implementation
  • 1. Include the filter_expression parameter
  • 2. Implement Visitors
  • 3. Processing Filter Expressions
  • Example

Was this helpful?

Export as PDF
  1. BDK API Reference

Filter Expressions

Learn about implementing filter expressions in your BDK project.

Overview

Filter expressions enable you to define filter criteria for your procedures using the whose keyword. These expressions allow you to filter data based on conditions such as equality, comparisons, and more. For example:

get users from office365 whose email is "john@mail.org"

Implementation

1. Include the filter_expression parameter

To implement a filter expression, you need to provide the special filter_expression parameter in your procedure method definition. For example:

@procedure("to read some (*SMS* messages)")
def read_sms_messages(
    self,
    offset: Optional[int],
    limit: Optional[int],
    filter_expression: Optional[FilterExpression],
) -> List[SMSMessage]:
    """
    Read some SMS messages using the Twilio API.
    """

2. Implement Visitors

The FilterExpressionVisitor class is an abstract base class that defines methods for visiting different types of filter expressions. Each type of filter expression (binary, unary, value, noun phrase) is defined as a subclass of FilterExpression. You will need to define a class that implements these methods to handle filter expressions. For example:

class MyFilterVisitor(FilterExpressionVisitor):
    def visit_binary_expression(self, expression: FilterBinaryExpression) -> T:
        # Implement logic for handling binary expressions
        pass

    def visit_unary_expression(self, expression: FilterUnaryExpression) -> T:
        # Implement logic for handling unary expressions
        pass

    def visit_value(self, expression: ValueExpression) -> T:
        # Implement logic for handling value expressions
        pass

    def visit_noun_phrases(self, expression: NounPhrasesExpression) -> T:
        # Implement logic for handling noun phrases expressions
        pass

Below is an example implementation of the FilterExpressionVisitor class in the Twilio Book:

SENDER_NUMBER = NounPhrase.from_word_list(["sender", "number"])
RECIPIENT_NUMBER = NounPhrase.from_word_list(["recipient", "number"])
DATE_SENT = NounPhrase.from_word_list(["date", "sent"])


class SMSMessageFilter(FilterExpressionVisitor):
    """
    Extract filtering information from the expression
    """

    current_noun_phrase: Optional[NounPhrase] = None
    current_value: Optional[Any] = None

    recipient_number: Union[str, object] = values.unset
    sender_number: Union[str, object] = values.unset
    date_sent: Union[datetime, object] = values.unset
    date_sent_before: Union[datetime, object] = values.unset
    date_sent_after: Union[datetime, object] = values.unset

    def visit_binary_expression(self, expression: FilterBinaryExpression):
        expression.left.accept(self)
        expression.right.accept(self)

        if expression.operator == FilterBinaryOperator.EQUALS:
            if self.current_noun_phrase == SENDER_NUMBER:
                self.sender_number = str(self.current_value)
            elif self.current_noun_phrase == RECIPIENT_NUMBER:
                self.recipient_number = str(self.current_value)
            elif self.current_noun_phrase == DATE_SENT:
                if not isinstance(self.current_value, datetime):
                    raise TypeMismatchError("date_sent", ConceptScalarType.DATETIME)

                self.date_sent = self.current_value
        elif expression.operator == FilterBinaryOperator.GREATER_THAN:
            if self.current_noun_phrase == DATE_SENT:
                if not isinstance(self.current_value, datetime):
                    raise TypeMismatchError("date_sent", ConceptScalarType.DATETIME)

                self.date_sent_after = self.current_value
        elif expression.operator == FilterBinaryOperator.LESS_THAN:
            if self.current_noun_phrase == DATE_SENT:
                if not isinstance(self.current_value, datetime):
                    raise TypeMismatchError("date_sent", ConceptScalarType.DATETIME)

                self.date_sent_before = self.current_value
        elif expression.operator == FilterBinaryOperator.AND:
            pass
        else:
            raise ValueError(f"unsupported filtering operator: {expression.operator}")

    def visit_unary_expression(self, expression: FilterUnaryExpression):
        pass

    def visit_value(self, expression: ValueExpression):
        self.current_value = expression.value

    def visit_noun_phrases(self, expression: NounPhrasesExpression):
        if len(expression.noun_phrases) != 1:
            raise ValueError(
                f"unsupported filtering noun phrase: {expression.noun_phrases}"
            )

        if expression.noun_phrases[0] not in [
            SENDER_NUMBER,
            RECIPIENT_NUMBER,
            DATE_SENT,
        ]:
            raise ValueError(
                f"unsupported filtering noun phrase: {expression.noun_phrases}"
            )

        self.current_noun_phrase = expression.noun_phrases[0]

3. Processing Filter Expressions

Once you’ve defined your visitor class, you need to pass an instance of it to the filter expression. This is done by calling accept onfilter_expression. For example:

visitor = MyFilterVisitor()
filter_expression.accept(visitor)

Example

In this example, a filter expression is used in the read some SMS messages procedure:

@procedure("to read some (*SMS* messages)")
def read_sms_messages(
    self,
    offset: Optional[int],
    limit: Optional[int],
    filter_expression: Optional[FilterExpression],
) -> List[SMSMessage]:
    """
    Read some SMS messages using the Twilio API.

    Returns:
        A list of SMS messages that matches the specified filtering criteria

    Example 1:
        Retrieve SMS messages filtered by sender and recipient numbers

        >>> read some sms messages whose sender number is "+18004445555" and whose recipient number is "+18004446666"

    Example 2:
        Retrieve SMS messages filtered by the date in which they were sent

        >>> convert "2022-03-01T15:00:00Z" to a datetime
        >>> use the above as the message date
        >>> read some sms messages whose date sent is the message date

    Example 3:
        Retrieve SMS messages that were sent in the specified time period

        >>> convert "2022-03-01T15:00:00Z" to a datetime
        >>> use the above as the start date
        >>> convert "2022-03-03T15:00:00Z" to a datetime
        >>> use the above as the end date
        >>> read some sms messages whose date sent is after the start date and whose date sent is before the end date
    """
    client = Client(self._account_sid, self._auth_token)

    filter_visitor = SMSMessageFilter()

    if filter_expression is not None:
        filter_expression.accept(filter_visitor)

    messages = client.messages.list(
        to=filter_visitor.recipient_number,
        from_=filter_visitor.sender_number,
        date_sent=filter_visitor.date_sent,
        date_sent_before=filter_visitor.date_sent_before,
        date_sent_after=filter_visitor.date_sent_after,
    )

    if offset is not None:
        messages = messages[offset:]

    if limit is not None:
        messages = messages[:limit]

    return [SMSMessage.from_message_instance(message) for message in messages]

Last updated 1 month ago

Was this helpful?