filter/filterAnd - Performs a filter and returns a QueryableList object of the same type.

                All the provided filters must match for the item to be returned.

            @params are in the format of fieldName__operation=value  where fieldName is the name of the field on any given item, "operation" is one of the given operations (@see main documentation) (e.x. eq, ne, isnull), and value is what is used in the operation.

            @return - A QueryableList object of the same type, with only the matching objects returned.

def filterAnd(self, **kwargs):
        '''
            filter/filterAnd - Performs a filter and returns a QueryableList object of the same type.

                All the provided filters must match for the item to be returned.

            @params are in the format of fieldName__operation=value  where fieldName is the name of the field on any given item, "operation" is one of the given operations (@see main documentation) (e.x. eq, ne, isnull), and value is what is used in the operation.

            @return - A QueryableList object of the same type, with only the matching objects returned.
        '''
        filters = getFiltersFromArgs(kwargs)
        ret = self.__class__()

        if USE_CACHED:
            caches = [dict() for i in range(len(self))]
            get_item_value = self._getItemValueFunction(caches, self._get_item_value)
        else:
            get_item_value = self._get_item_value

        # AND loop - for each item in this collection, run through each of the filter types.
        # If any of the filter types do not match, move on to next item
        # If all filters match, add item to the return set
        for item in self:
            keepIt = True

            # Do is/isnot first (and implicitly, isnull)
            for fieldName, value in filters['is']:
                if get_item_value(item, fieldName) is not value:
                    keepIt = False
                    break

            if keepIt is False:
                continue

            for fieldName, value in filters['isnot']:
                if get_item_value(item, fieldName) is value:
                    keepIt = False
                    break

            if keepIt is False:
                continue

            for fieldName, matchFunc in filters['customMatch']:
                val = get_item_value(item, fieldName)
                if not matchFunc(val):
                    keepIt = False
                    break

            if keepIt is False:
                continue

            for fieldName, value in filters['in']:
                if get_item_value(item, fieldName) not in value:
                    keepIt = False
                    break

            if keepIt is False:
                continue

            for fieldName, value in filters['notin']:
                if get_item_value(item, fieldName) in value:
                    keepIt = False
                    break

            if keepIt is False:
                continue


            for fieldName, value in filters['eq']:
                if get_item_value(item, fieldName) != value:
                    keepIt = False
                    break

            if keepIt is False:
                continue

            for fieldName, value in filters['ieq']:
                # If we can't lowercase the item's value, it obviously doesn't match whatever we previously could.
                # Reminder: the "i" filter's values have already been lowercased
                itemValue = get_item_value(item, fieldName)
                try:
                    itemValueLower = itemValue.lower()
                except:
                    keepIt = False
                    break

                if itemValueLower != value:
                    keepIt = False
                    break

            if keepIt is False:
                continue

            for fieldName, value in filters['ne']:
                if get_item_value(item, fieldName) == value:
                    keepIt = False
                    break

            if keepIt is False:
                continue

            for fieldName, value in filters['ine']:
                itemValue = get_item_value(item, fieldName)
                try:
                    itemValueLower = itemValue.lower()
                except:
                    # If we can't convert the field value to lowercase, it does not equal the other.
                    continue

                if itemValueLower == value:
                    keepIt = False
                    break

            if keepIt is False:
                continue

            for fieldName, value in filters['lt']:
                if get_item_value(item, fieldName) >= value:
                    keepIt = False
                    break

            if keepIt is False:
                continue

            for fieldName, value in filters['lte']:
                if get_item_value(item, fieldName) > value:
                    keepIt = False
                    break

            if keepIt is False:
                continue

            for fieldName, value in filters['gt']:
                if get_item_value(item, fieldName) <= value:
                    keepIt = False
                    break

            if keepIt is False:
                continue


            for fieldName, value in filters['gte']:
                if get_item_value(item, fieldName) < value:
                    keepIt = False
                    break

            if keepIt is False:
                continue

            for fieldName, value in filters['contains']:
                itemValue = get_item_value(item, fieldName)
                try:
                    if value not in itemValue:
                        keepIt = False
                        break
                except:
                    # If field does not support "in", it does not contain the item.
                    keepIt = False
                    break

            if keepIt is False:
                continue

            for fieldName, value in filters['icontains']:
                itemValue = get_item_value(item, fieldName)
                try:
                    itemValue = itemValue.lower()

                    if value not in itemValue:
                        keepIt = False
                        break
                except:
                    keepIt = False
                    break

            if keepIt is False:
                continue

            for fieldName, value in filters['notcontains']:
                itemValue = get_item_value(item, fieldName)
                try:
                    if value in itemValue:
                        keepIt = False
                        break
                except:
                    # If field does not support "in", it does not contain the item.
                    continue

            if keepIt is False:
                continue

            for fieldName, value in filters['noticontains']:
                itemValue = get_item_value(item, fieldName)
                try:
                    itemValue = itemValue.lower()

                    if value in itemValue:
                        keepIt = False
                        break
                except:
                    continue


            if keepIt is False:
                continue

            for fieldName, value in filters['containsAny']:
                itemValue = get_item_value(item, fieldName)

                if itemValue is None:
                    # Cannot split None
                    keepIt = False
                    break

                didContain = False
                for maybeContains in value:
                    if maybeContains in itemValue:
                        didContain = True
                        break
                if didContain is False:
                    keepIt = False
                    break

            if keepIt is False:
                continue

            for fieldName, value in filters['notcontainsAny']:
                itemValue = get_item_value(item, fieldName)

                if itemValue is None:
                    # Cannot split None, so it is a match..
                    continue

                didContain = False
                for maybeContains in value:
                    if maybeContains in itemValue:
                        didContain = True
                        break
                if didContain is True:
                    keepIt = False
                    break

            if keepIt is False:
                continue

            # IDEA: Could implement a dict here of last several splits, incase we have repeated splits on same large field.
            #   I think this may be more lossy in the general case to support a corner case though.

            for fieldName, value in filters['splitcontains']:
                (splitBy, maybeContains) = value

                itemValue = get_item_value(item, fieldName)

                if itemValue is None:
                    # Cannot split, no match
                    keepIt = False
                    break

                try:
                    itemValue = itemValue.split(splitBy)
                    if maybeContains not in itemValue:
                        keepIt = False
                        break
                except:
                    # If field does not supprt "in", or cannot be split, it does not contain the item.
                    keepIt = False
                    break

            if keepIt is False:
                continue

            for fieldName, value in filters['splitnotcontains']:
                (splitBy, maybeContains) = value

                itemValue = get_item_value(item, fieldName)

                if itemValue is None:
                    # Cannot split, so does not contain and is a match.
                    continue

                try:
                    itemValue = itemValue.split(splitBy)
                    if maybeContains in itemValue:
                        keepIt = False
                        break
                except:
                    # If field does not supprt "in", or cannot be split, it does not contain the item and thus matches here.
                    continue

            if keepIt is False:
                continue

            for fieldName, value in filters['splitcontainsAny']:
                (splitBy, maybeContainsLst) = value

                itemValue = get_item_value(item, fieldName)

                if itemValue is None:
                    # Cannot split, so it does not contain a match
                    keepIt = False
                    break

                try:
                    itemValue = itemValue.split(splitBy)
                except:
                    # Cannot split, does not match.
                    keepIt = False
                    break


                didContain = False
                for maybeContains in maybeContainsLst:
                    if maybeContains in itemValue:
                        didContain = True
                        break
                if didContain is False:
                    keepIt = False
                    break

            if keepIt is False:
                continue

            for fieldName, value in filters['splitnotcontainsAny']:
                (splitBy, maybeContainsLst) = value

                itemValue = get_item_value(item, fieldName)

                if itemValue is None:
                    # Cannot split, so it must not contain any (and is a match)
                    continue

                try:
                    itemValue = itemValue.split(splitBy)
                except:
                    # Cannot split, so must not contain any (and is a match)
                    continue

                didContain = False
                for maybeContains in maybeContainsLst:
                    if maybeContains in itemValue:
                        didContain = True
                        break
                if didContain is True:
                    keepIt = False
                    break

            if keepIt is False:
                continue

            # All the way through all filters, the item matches.
            ret.append(item)

        return ret