def matchExpression(fieldName, matchString, matchType, matchFlags):
                # special case recordType field
                if fieldName == FieldName.recordType:
                    # change kind to record type
                    matchValue = vCardKindToRecordTypeMap.get(matchString.lower())
                    if matchValue is None:
                        matchValue = NamedConstant()
                        matchValue.description = u""

                    # change types and flags
                    matchFlags &= ~MatchFlags.caseInsensitive
                    matchType = MatchType.equals
                else:
                    matchValue = matchString.decode("utf-8")

                return MatchExpression(fieldName, matchValue, matchType, matchFlags)
            def matchExpression(fieldName, matchString, matchType, matchFlags):
                # special case recordType field
                if fieldName == FieldName.recordType:
                    # change kind to record type
                    matchValue = vCardKindToRecordTypeMap.get(matchString.lower())
                    if matchValue is None:
                        matchValue = NamedConstant()
                        matchValue.description = u""

                    # change types and flags
                    matchFlags &= ~MatchFlags.caseInsensitive
                    matchType = MatchType.equals
                else:
                    matchValue = matchString.decode("utf-8")

                return MatchExpression(fieldName, matchValue, matchType, matchFlags)
    def doAddressBookDirectoryQuery(self, addressBookFilter, addressBookQuery, maxResults, defaultKind="individual"):
        """
        Get vCards for a given addressBookFilter and addressBookQuery
        """

        log.debug("doAddressBookDirectoryQuery: directory={directory} addressBookFilter={addressBookFilter}, addressBookQuery={addressBookQuery}, maxResults={maxResults}",
                  directory=self.directory, addressBookFilter=addressBookFilter, addressBookQuery=addressBookQuery, maxResults=maxResults)
        results = []
        limited = False
        maxQueryRecords = 0

        vcardPropToRecordFieldMap = {
            "FN": FieldName.fullNames,
            "N": FieldName.fullNames,
            "EMAIL": FieldName.emailAddresses,
            "UID": FieldName.uid,
            "ADR": (
                    CalFieldName.streetAddress,
                    CalFieldName.floor,
                    ),
            "KIND": FieldName.recordType,
            # LATER "X-ADDRESSBOOKSERVER-MEMBER": FieldName.membersUIDs,
        }

        propNames, expression = expressionFromABFilter(
            addressBookFilter, vcardPropToRecordFieldMap, vCardConstantProperties,
        )

        if expression:

            queryRecordType = None
            if "KIND" not in propNames:
                queryRecordType = vCardKindToRecordTypeMap.get(defaultKind)

            # if CompoundExpression of MatchExpression: recordsWithFieldValue() else recordsMatchingType()
            fields = []
            if expression is not True:

                def fieldForMatchExpression(match):
                    return (
                        match.fieldName.name,
                        match.fieldValue,
                        match.flags,
                        match.matchType,
                    )

                if isinstance(expression, CompoundExpression):
                    operand = expression.operand
                    for match in expression.expressions:
                        if isinstance(match, MatchExpression):
                            if match.fieldName != FieldName.recordType:
                                fields.append(fieldForMatchExpression(match))
                            # else optimize: collect record type list for query
                        else:
                            # do all record types query
                            fields = []
                            break
                elif isinstance(expression, MatchExpression):
                    operand = Operand.OR
                    if expression.fieldName != FieldName.recordType:
                        fields.append(fieldForMatchExpression(expression))
                    else:
                        recordType = expression.fieldValue

            maxRecords = int(maxResults * 1.2)

            # keep trying query till we get results based on filter.  Especially when doing "all results" query
            while True:
                queryLimited = False

                log.debug("doAddressBookDirectoryQuery: expression={expression!r}, propNames={propNames}", expression=expression, propNames=propNames)

                allRecords = set()
                if fields:
                    records = yield self.directory.recordsMatchingFields(fields, operand, queryRecordType)
                    log.debug("doAddressBookDirectoryQuery: recordsMatchingFields({f}, {o}): #records={n}, records={records!r}",
                              f=fields, o=operand, n=len(records), records=records)
                    allRecords = set(records)
                else:
                    recordTypes = set([queryRecordType]) if queryRecordType else set(self.directory.recordTypes()) & set(recordTypeToVCardKindMap.keys())
                    for recordType in recordTypes:
                        records = yield self.directory.recordsWithRecordType(recordType)
                        log.debug("doAddressBookDirectoryQuery: #records={n}, records={records!r}", n=len(records), records=records)
                        allRecords |= set(records)

                vCardsResults = [(yield ABDirectoryQueryResult(self).generate(record)) for record in allRecords]

                filteredResults = set()
                for vCardResult in vCardsResults:
                    if addressBookFilter.match(vCardResult.vCard()):
                        log.debug("doAddressBookDirectoryQuery: vCard did match filter:\n{vcard}", vcard=vCardResult.vCard())
                        filteredResults.add(vCardResult)
                    else:
                        log.debug("doAddressBookDirectoryQuery: vCard did not match filter:\n{vcard}", vcard=vCardResult.vCard())

                #no more results
                if not queryLimited:
                    break

                # more than requested results
                if maxResults and len(filteredResults) >= maxResults:
                    break

                # more than max report results
                if len(filteredResults) >= config.MaxQueryWithDataResults:
                    break

                # more than self limit
                if maxQueryRecords and maxRecords >= maxQueryRecords:
                    break

                # try again with 2x
                maxRecords *= 2
                if maxQueryRecords and maxRecords > maxQueryRecords:
                    maxRecords = maxQueryRecords

            results = sorted(filteredResults, key=lambda result: result.vCard().propertyValue("UID"))
            limited = maxResults and len(results) >= maxResults

        log.info("limited={l} #results={n}", l=limited, n=len(results))
        returnValue((results, limited,))
    def doAddressBookDirectoryQuery(self,
                                    addressBookFilter,
                                    addressBookQuery,
                                    maxResults,
                                    defaultKind="individual"):
        """
        Get vCards for a given addressBookFilter and addressBookQuery
        """

        log.debug(
            "doAddressBookDirectoryQuery: directory={directory} addressBookFilter={addressBookFilter}, addressBookQuery={addressBookQuery}, maxResults={maxResults}",
            directory=self.directory,
            addressBookFilter=addressBookFilter,
            addressBookQuery=addressBookQuery,
            maxResults=maxResults)
        results = []
        limited = False
        maxQueryRecords = 0

        vcardPropToRecordFieldMap = {
            "FN": FieldName.fullNames,
            "N": FieldName.fullNames,
            "EMAIL": FieldName.emailAddresses,
            "UID": FieldName.uid,
            "ADR": (
                CalFieldName.streetAddress,
                # CalFieldName.floor,
            ),
            "KIND": FieldName.recordType,
            # LATER "X-ADDRESSBOOKSERVER-MEMBER": FieldName.membersUIDs,
        }

        propNames, expression = expressionFromABFilter(
            addressBookFilter,
            vcardPropToRecordFieldMap,
            vCardConstantProperties,
        )

        if expression:

            queryRecordType = None
            if "KIND" not in propNames:
                queryRecordType = vCardKindToRecordTypeMap.get(defaultKind)

            # if CompoundExpression of MatchExpression: recordsWithFieldValue() else recordsMatchingType()
            fields = []
            if expression is not True:

                def fieldForMatchExpression(match):
                    return (
                        match.fieldName.name,
                        match.fieldValue,
                        match.flags,
                        match.matchType,
                    )

                if isinstance(expression, CompoundExpression):
                    operand = expression.operand
                    for match in expression.expressions:
                        if isinstance(match, MatchExpression):
                            if match.fieldName != FieldName.recordType:
                                fields.append(fieldForMatchExpression(match))
                            # else optimize: collect record type list for query
                        else:
                            # do all record types query
                            fields = []
                            break
                elif isinstance(expression, MatchExpression):
                    operand = Operand.OR
                    if expression.fieldName != FieldName.recordType:
                        fields.append(fieldForMatchExpression(expression))
                    else:
                        recordType = expression.fieldValue

            maxRecords = int(maxResults * 1.2)

            # keep trying query till we get results based on filter.  Especially when doing "all results" query
            while True:
                queryLimited = False

                log.debug(
                    "doAddressBookDirectoryQuery: expression={expression!r}, propNames={propNames}",
                    expression=expression,
                    propNames=propNames)

                allRecords = set()
                if fields:
                    records = yield self.directory.recordsMatchingFields(
                        fields, operand, queryRecordType)
                    log.debug(
                        "doAddressBookDirectoryQuery: recordsMatchingFields({f}, {o}): #records={n}, records={records!r}",
                        f=fields,
                        o=operand,
                        n=len(records),
                        records=records)
                    allRecords = set(records)
                else:
                    recordTypes = set([queryRecordType
                                       ]) if queryRecordType else set(
                                           self.directory.recordTypes()) & set(
                                               recordTypeToVCardKindMap.keys())
                    for recordType in recordTypes:
                        records = yield self.directory.recordsWithRecordType(
                            recordType)
                        log.debug(
                            "doAddressBookDirectoryQuery: #records={n}, records={records!r}",
                            n=len(records),
                            records=records)
                        allRecords |= set(records)

                vCardsResults = [
                    (yield ABDirectoryQueryResult(self).generate(record))
                    for record in allRecords
                ]

                filteredResults = set()
                for vCardResult in vCardsResults:
                    if addressBookFilter.match(vCardResult.vCard()):
                        log.debug(
                            "doAddressBookDirectoryQuery: vCard did match filter:\n{vcard}",
                            vcard=vCardResult.vCard())
                        filteredResults.add(vCardResult)
                    else:
                        log.debug(
                            "doAddressBookDirectoryQuery: vCard did not match filter:\n{vcard}",
                            vcard=vCardResult.vCard())

                # no more results
                if not queryLimited:
                    break

                # more than requested results
                if maxResults and len(filteredResults) >= maxResults:
                    break

                # more than max report results
                if len(filteredResults) >= config.MaxQueryWithDataResults:
                    break

                # more than self limit
                if maxQueryRecords and maxRecords >= maxQueryRecords:
                    break

                # try again with 2x
                maxRecords *= 2
                if maxQueryRecords and maxRecords > maxQueryRecords:
                    maxRecords = maxQueryRecords

            results = sorted(
                filteredResults,
                key=lambda result: result.vCard().propertyValue("UID"))
            limited = maxResults and len(results) >= maxResults

        log.info("limited={l} #results={n}", l=limited, n=len(results))
        returnValue((
            results,
            limited,
        ))