public List<Stream> match(Message message) {
        final Set<Stream> result = Sets.newHashSet();
        final Set<String> blackList = Sets.newHashSet();

        for (final Rule rule : rulesList) {
            if (blackList.contains(rule.getStreamId())) {
                continue;
            }

            final StreamRule streamRule = rule.getStreamRule();
            final StreamRuleType streamRuleType = streamRule.getType();
            final Stream.MatchingType matchingType = rule.getMatchingType();
            if (!ruleTypesNotNeedingFieldPresence.contains(streamRuleType)
                    && !message.hasField(streamRule.getField())) {
                if (matchingType == Stream.MatchingType.AND) {
                    result.remove(rule.getStream());
                    // blacklist stream because it can't match anymore
                    blackList.add(rule.getStreamId());
                }

                continue;
            }

            final Stream stream;
            if (streamRuleType != StreamRuleType.REGEX) {
                stream = rule.match(message);
            } else {
                stream = rule.matchWithTimeOut(message, streamProcessingTimeout, TimeUnit.MILLISECONDS);
            }

            if (stream == null) {
                if (matchingType == Stream.MatchingType.AND) {
                    result.remove(rule.getStream());
                    // blacklist stream because it can't match anymore
                    blackList.add(rule.getStreamId());
                }
            } else {
                result.add(stream);
                if (matchingType == Stream.MatchingType.OR) {
                    // blacklist stream because it is already matched
                    blackList.add(rule.getStreamId());
                }
            }
        }

        final Stream defaultStream = defaultStreamProvider.get();
        boolean alreadyRemovedDefaultStream = false;
        for (Stream stream : result) {
            if (stream.getRemoveMatchesFromDefaultStream()) {
                if (alreadyRemovedDefaultStream || message.removeStream(defaultStream)) {
                    alreadyRemovedDefaultStream = true;
                    if (LOG.isTraceEnabled()) {
                        LOG.trace("Successfully removed default stream <{}> from message <{}>", defaultStream.getId(), message.getId());
                    }
                } else {
                    // A previously executed message processor (or Illuminate) has likely already removed the
                    // default stream from the message. Now, the message has matched a stream in the Graylog
                    // MessageFilterChain, and the matching stream is also set to remove the default stream.
                    // This is usually from user-defined stream rules, and is generally not a problem.
                    cannotRemoveDefaultMeter.inc();
                    if (LOG.isTraceEnabled()) {
                        LOG.trace("Couldn't remove default stream <{}> from message <{}>", defaultStream.getId(), message.getId());
                    }
                }
            }
        }

        return ImmutableList.copyOf(result);
    }
@Test
    public void testInvertedContainsMatch() throws Exception {
        final StreamMock stream = getStreamMock("test");
        final StreamRuleMock rule = new StreamRuleMock(
                ImmutableMap.<String, Object>builder()
                        .put("_id", new ObjectId())
                        .put("field", "testfield")
                        .put("inverted", true)
                        .put("value", "testvalue")
                        .put("type", StreamRuleType.CONTAINS.toInteger())
                        .put("stream_id", stream.getId())
                        .build()
        );

        stream.setStreamRules(Lists.newArrayList(rule));

        final StreamRouterEngine engine = newEngine(Lists.newArrayList(stream));
        final Message message = getMessage();

        // Without the field
        assertEquals(Lists.newArrayList(stream), engine.match(message));

        // Without the matching value in the field
        message.addField("testfield", "no-foobar");

        assertEquals(Lists.newArrayList(stream), engine.match(message));

        // With matching value in the field.
        message.addField("testfield", "hello testvalue");

        assertTrue(engine.match(message).isEmpty());
    }