/** * Gets the result from the data presented to the sketch as a {@link Clip}. Also adds {@link Meta} if * asked for. * * @param metaKey If set to a non-null value, Sketch metadata will be added to the result. * @param conceptKeys If provided, these {@link Concept} names will be added to the metadata. * @return A {@link Clip} of the results. */ public Clip getResult(String metaKey, Map<String, String> conceptKeys) { // Subclasses are charge of adding data. We'll just create an empty Clip with the metadata. return Clip.of(getMetadata(metaKey, conceptKeys)); }
/** * Gets the aggregated records so far since the last call to {@link #reset()}. The records have a size that is at * most the maximum specified by the {@link Aggregation}. * * @return a {@link Clip} of the records so far. */ @Override public Clip getResult() { return Clip.of(getRecords()); }
@Override public Clip getResult() { return Clip.of(getRecords()); }
/** * Write this error as a JSON Bullet error response in the {@link Meta} of a {@link Clip}. * * @return A String JSON version of this error. */ public String asJSONClip() { return Clip.of(Meta.of(this)).asJSON(); } }
/** * Write this error as a JSON Bullet error response in the {@link Meta} of a {@link Clip}. * * @return A String JSON version of this error. */ public String asJSONClip() { return Clip.of(Meta.of(this)).asJSON(); } }
@Override public Clip getResult() { // This has already called aggregation.getMetadata Clip clip = Clip.of(getMetadata()); clip.add(aggregation.getRecords()); return clip; }
private void emitErrorsAsResult(String id, Metadata metadata, List<BulletError> errors) { updateCount(improperQueriesCount, 1L); emitResult(id, withSignal(metadata, Metadata.Signal.FAIL), Clip.of(Meta.of(errors))); }
private void emitErrorsAsResult(String id, Metadata metadata, List<BulletError> errors) { updateCount(improperQueriesCount, 1L); emitResult(id, withSignal(metadata, Metadata.Signal.FAIL), Clip.of(Meta.of(errors))); }
@Test public void testFailJoiningForNoQuery() { List<BulletRecord> sent = sendRawRecordTuplesTo(bolt, "42"); Tuple expected = TupleUtils.makeTuple(TupleClassifier.Type.RESULT_TUPLE, "42", Clip.of(sent).asJSON(), COMPLETED); Assert.assertFalse(wasResultEmitted(expected)); Assert.assertEquals(collector.getEmittedCount(), 0); }
@Test public void testJoining() { Tuple query = TupleUtils.makeIDTuple(TupleClassifier.Type.QUERY_TUPLE, "42", "{}", EMPTY); bolt.execute(query); List<BulletRecord> sent = sendRawRecordTuplesTo(bolt, "42"); Tuple expected = TupleUtils.makeTuple(TupleClassifier.Type.RESULT_TUPLE, "42", Clip.of(sent).asJSON(), COMPLETED); Assert.assertTrue(wasResultEmittedTo(TopologyConstants.RESULT_STREAM, expected)); Tuple metadata = TupleUtils.makeTuple(TupleClassifier.Type.FEEDBACK_TUPLE, "42", new Metadata(Metadata.Signal.COMPLETE, null)); Assert.assertTrue(wasMetadataEmittedTo(TopologyConstants.FEEDBACK_STREAM, metadata)); Assert.assertEquals(collector.getAllEmittedTo(TopologyConstants.RESULT_STREAM).count(), 1); Assert.assertEquals(collector.getAllEmittedTo(TopologyConstants.FEEDBACK_STREAM).count(), 1); }
@Test public void testRawQueryDoneButNotTimedOutWithExcessRecords() { Tuple query = TupleUtils.makeIDTuple(TupleClassifier.Type.QUERY_TUPLE, "42", makeAggregationQuery(RAW, 5), EMPTY); bolt.execute(query); // This will send 2 batches of 3 records each (total of 6 records). List<BulletRecord> sent = sendRawRecordTuplesTo(bolt, "42", 5, 3); List<BulletRecord> actualSent = sent.subList(0, 5); Tuple expected = TupleUtils.makeTuple(TupleClassifier.Type.RESULT_TUPLE, "42", Clip.of(actualSent).asJSON(), COMPLETED); Assert.assertTrue(wasResultEmittedTo(TopologyConstants.RESULT_STREAM, expected)); Tuple metadata = TupleUtils.makeTuple(TupleClassifier.Type.FEEDBACK_TUPLE, "42", new Metadata(Metadata.Signal.COMPLETE, null)); Assert.assertTrue(wasMetadataEmittedTo(TopologyConstants.FEEDBACK_STREAM, metadata)); Assert.assertEquals(collector.getAllEmittedTo(TopologyConstants.RESULT_STREAM).count(), 1); Assert.assertEquals(collector.getAllEmittedTo(TopologyConstants.FEEDBACK_STREAM).count(), 1); }
@Test public void testErrorInQueryWithoutMetadata() { Tuple query = TupleUtils.makeIDTuple(TupleClassifier.Type.QUERY_TUPLE, "42", "{\"aggregation\": {\"type\": \"garbage\"}}"); bolt.execute(query); Assert.assertEquals(collector.getEmittedCount(), 1); BulletError expectedError = Aggregation.TYPE_NOT_SUPPORTED_ERROR; Meta expectedMetadata = Meta.of(expectedError); List<Object> expected = TupleUtils.makeTuple("42", Clip.of(expectedMetadata).asJSON(), FAILED).getValues(); List<Object> actual = collector.getNthTupleEmittedTo(TopologyConstants.RESULT_STREAM, 1).get(); Assert.assertTrue(isSameResult(actual, expected)); Assert.assertEquals(collector.getAllEmittedTo(TopologyConstants.RESULT_STREAM).count(), 1); Assert.assertEquals(collector.getEmittedCount(), 1); }
@Test public void testMissingMetadataIsEmitted() { Tuple query = TupleUtils.makeIDTuple(TupleClassifier.Type.QUERY_TUPLE, "42", "{}"); bolt.execute(query); List<BulletRecord> sent = sendRawRecordTuplesTo(bolt, "42", RAW_MAX_SIZE); Assert.assertEquals(collector.getEmittedCount(), 2); Tuple expected = TupleUtils.makeTuple(TupleClassifier.Type.RESULT_TUPLE, "42", Clip.of(sent).asJSON(), COMPLETED); Assert.assertTrue(wasResultEmittedTo(TopologyConstants.RESULT_STREAM, expected)); Tuple metadata = TupleUtils.makeTuple(TupleClassifier.Type.FEEDBACK_TUPLE, "42", new Metadata(Metadata.Signal.COMPLETE, null)); Assert.assertTrue(wasMetadataEmittedTo(TopologyConstants.FEEDBACK_STREAM, metadata)); }
@Test public void testErrorEmittedProperly() { Tuple query = TupleUtils.makeIDTuple(TupleClassifier.Type.QUERY_TUPLE, "42", "garbage", EMPTY); bolt.execute(query); Assert.assertEquals(collector.getEmittedCount(), 1); String error = ParsingError.GENERIC_JSON_ERROR + ":\ngarbage\n" + "IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $"; BulletError expectedError = ParsingError.makeError(error, ParsingError.GENERIC_JSON_RESOLUTION); Meta expectedMetadata = Meta.of(expectedError); List<Object> expected = TupleUtils.makeTuple("42", Clip.of(expectedMetadata).asJSON(), FAILED).getValues(); List<Object> actual = collector.getNthTupleEmittedTo(TopologyConstants.RESULT_STREAM, 1).get(); Assert.assertTrue(isSameResult(actual, expected)); }
@Test public void testUnknownAggregation() { // Lowercase "top" is not valid and will not be parsed since there is no enum for it // In this case aggregation type should be set to null and an error should be emitted Tuple query = TupleUtils.makeIDTuple(TupleClassifier.Type.QUERY_TUPLE, "42", "{\"aggregation\": {\"type\": \"garbage\"}}", EMPTY); bolt.execute(query); Assert.assertEquals(collector.getEmittedCount(), 1); BulletError expectedError = Aggregation.TYPE_NOT_SUPPORTED_ERROR; Meta expectedMetadata = Meta.of(expectedError); List<Object> expected = TupleUtils.makeTuple("42", Clip.of(expectedMetadata).asJSON(), FAILED).getValues(); List<Object> actual = collector.getNthTupleEmittedTo(TopologyConstants.RESULT_STREAM, 1).get(); Assert.assertTrue(isSameResult(actual, expected)); }
@Test public void testDataWithoutQuery() { sendRawRecordTuplesTo(bolt, "42", RAW_MAX_SIZE - 2); Assert.assertEquals(collector.getEmittedCount(), 0); Tuple query = TupleUtils.makeIDTuple(TupleClassifier.Type.QUERY_TUPLE, "42", "{}"); bolt.execute(query); List<BulletRecord> sent = sendRawRecordTuplesTo(bolt, "42", RAW_MAX_SIZE); Assert.assertEquals(collector.getEmittedCount(), 2); Tuple expected = TupleUtils.makeTuple(TupleClassifier.Type.RESULT_TUPLE, "42", Clip.of(sent).asJSON(), COMPLETED); Assert.assertTrue(wasResultEmittedTo(TopologyConstants.RESULT_STREAM, expected)); Tuple metadata = TupleUtils.makeTuple(TupleClassifier.Type.FEEDBACK_TUPLE, "42", new Metadata(Metadata.Signal.COMPLETE, null)); Assert.assertTrue(wasMetadataEmittedTo(TopologyConstants.FEEDBACK_STREAM, metadata)); }
@Test public void testMetadataIsNotReplaced() { Metadata actualMetadata = new Metadata(null, "foo"); Tuple query = TupleUtils.makeIDTuple(TupleClassifier.Type.QUERY_TUPLE, "42", "{}", actualMetadata); bolt.execute(query); List<BulletRecord> sent = sendRawRecordTuplesTo(bolt, "42", RAW_MAX_SIZE); Metadata expectedMetadata = new Metadata(Metadata.Signal.COMPLETE, "foo"); Assert.assertEquals(collector.getEmittedCount(), 2); Tuple expected = TupleUtils.makeTuple(TupleClassifier.Type.RESULT_TUPLE, "42", Clip.of(sent).asJSON(), expectedMetadata); Assert.assertTrue(wasResultEmittedTo(TopologyConstants.RESULT_STREAM, expected)); Tuple metadata = TupleUtils.makeTuple(TupleClassifier.Type.FEEDBACK_TUPLE, "42", new Metadata(Metadata.Signal.COMPLETE, "foo")); Assert.assertTrue(wasMetadataEmittedTo(TopologyConstants.FEEDBACK_STREAM, metadata)); }
@Test public void testUnhandledExceptionErrorEmitted() { // An empty query should throw an null-pointer exception which should be caught in JoinBolt // and an error should be emitted Tuple query = TupleUtils.makeIDTuple(TupleClassifier.Type.QUERY_TUPLE, "42", "", EMPTY); bolt.execute(query); sendRawRecordTuplesTo(bolt, "42"); Assert.assertEquals(collector.getEmittedCount(), 1); String error = ParsingError.GENERIC_JSON_ERROR + ":\n\nNullPointerException: "; BulletError expectedError = ParsingError.makeError(error, ParsingError.GENERIC_JSON_RESOLUTION); Meta expectedMetadata = Meta.of(expectedError); List<Object> expected = TupleUtils.makeTuple("42", Clip.of(expectedMetadata).asJSON(), FAILED).getValues(); List<Object> actual = collector.getNthTupleEmittedTo(TopologyConstants.RESULT_STREAM, 1).get(); Assert.assertTrue(isSameResult(actual, expected)); }
@Test public void testQueryIdentifierMetadata() { config = configWithRawMaxAndEmptyMeta(); enableMetadataInConfig(config, Concept.QUERY_METADATA.getName(), "meta"); enableMetadataInConfig(config, Concept.QUERY_ID.getName(), "id"); setup(new JoinBolt(config)); Tuple query = TupleUtils.makeIDTuple(TupleClassifier.Type.QUERY_TUPLE, "42", "{}", EMPTY); bolt.execute(query); List<BulletRecord> sent = sendRawRecordTuplesTo(bolt, "42"); Meta meta = new Meta(); meta.add("meta", singletonMap("id", "42")); Tuple expected = TupleUtils.makeTuple(TupleClassifier.Type.RESULT_TUPLE, "42", Clip.of(sent).add(meta).asJSON(), COMPLETED); Assert.assertTrue(wasResultEmittedTo(TopologyConstants.RESULT_STREAM, expected)); Tuple metadata = TupleUtils.makeTuple(TupleClassifier.Type.FEEDBACK_TUPLE, "42", new Metadata(Metadata.Signal.COMPLETE, null)); Assert.assertTrue(wasMetadataEmittedTo(TopologyConstants.FEEDBACK_STREAM, metadata)); Assert.assertEquals(collector.getAllEmittedTo(TopologyConstants.RESULT_STREAM).count(), 1); Assert.assertEquals(collector.getAllEmittedTo(TopologyConstants.FEEDBACK_STREAM).count(), 1); }
@Test public void testRateLimitingOnCombine() { RateLimitError rateLimitError = new RateLimitError(42.0, 5.0); bolt = new RateLimitedJoinBolt(2, rateLimitError, config); setup(bolt); Tuple query = TupleUtils.makeIDTuple(TupleClassifier.Type.QUERY_TUPLE, "42", makeAggregationQuery(RAW, 10)); bolt.execute(query); // After consuming the 3rd one, it is rate limited and the fourth is not consumed List<BulletRecord> sent = sendRawRecordTuplesTo(bolt, "42", 4); Assert.assertEquals(collector.getEmittedCount(), 2); Tuple expected = TupleUtils.makeTuple(TupleClassifier.Type.RESULT_TUPLE, "42", Clip.of(sent.subList(0, 3)).add(rateLimitError.makeMeta()).asJSON(), new Metadata(Metadata.Signal.FAIL, null)); Assert.assertTrue(wasResultEmittedTo(TopologyConstants.RESULT_STREAM, expected)); Tuple metadata = TupleUtils.makeTuple(TupleClassifier.Type.FEEDBACK_TUPLE, "42", new Metadata(Metadata.Signal.KILL, null)); Assert.assertTrue(wasMetadataEmittedTo(TopologyConstants.FEEDBACK_STREAM, metadata)); }