serde tests + equals/hashCode fixes for extraction functions

This commit is contained in:
Xavier Léauté 2015-03-10 15:09:59 -07:00
parent d3f5bddc5c
commit e01ed16030
12 changed files with 403 additions and 2 deletions

View File

@ -20,6 +20,7 @@ package io.druid.query.extraction;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.metamx.common.StringUtils;
import org.mozilla.javascript.Context;
@ -67,6 +68,8 @@ public class JavascriptExtractionFn implements ExtractionFn
@JsonProperty("function") String function
)
{
Preconditions.checkNotNull(function, "function must not be null");
this.function = function;
this.fn = compile(function);
}
@ -118,4 +121,29 @@ public class JavascriptExtractionFn implements ExtractionFn
"function='" + function + '\'' +
'}';
}
@Override
public boolean equals(Object o)
{
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
JavascriptExtractionFn that = (JavascriptExtractionFn) o;
if (!function.equals(that.function)) {
return false;
}
return true;
}
@Override
public int hashCode()
{
return function.hashCode();
}
}

View File

@ -19,6 +19,7 @@ package io.druid.query.extraction;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Preconditions;
import com.metamx.common.StringUtils;
import java.nio.ByteBuffer;
@ -39,6 +40,8 @@ public class MatchingDimExtractionFn extends DimExtractionFn
@JsonProperty("expr") String expr
)
{
Preconditions.checkNotNull(expr, "expr must not be null");
this.expr = expr;
this.pattern = Pattern.compile(expr);
}
@ -78,4 +81,29 @@ public class MatchingDimExtractionFn extends DimExtractionFn
{
return String.format("regex_matches(%s)", expr);
}
@Override
public boolean equals(Object o)
{
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
MatchingDimExtractionFn that = (MatchingDimExtractionFn) o;
if (!expr.equals(that.expr)) {
return false;
}
return true;
}
@Override
public int hashCode()
{
return expr.hashCode();
}
}

View File

@ -19,6 +19,7 @@ package io.druid.query.extraction;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Preconditions;
import com.metamx.common.StringUtils;
import java.nio.ByteBuffer;
@ -39,6 +40,8 @@ public class RegexDimExtractionFn extends DimExtractionFn
@JsonProperty("expr") String expr
)
{
Preconditions.checkNotNull(expr, "expr must not be null");
this.expr = expr;
this.pattern = Pattern.compile(expr);
}
@ -77,4 +80,29 @@ public class RegexDimExtractionFn extends DimExtractionFn
{
return String.format("regex(%s)", expr);
}
@Override
public boolean equals(Object o)
{
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
RegexDimExtractionFn that = (RegexDimExtractionFn) o;
if (!expr.equals(that.expr)) {
return false;
}
return true;
}
@Override
public int hashCode()
{
return expr.hashCode();
}
}

View File

@ -19,6 +19,7 @@ package io.druid.query.extraction;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Preconditions;
import io.druid.query.search.search.SearchQuerySpec;
import java.nio.ByteBuffer;
@ -36,6 +37,8 @@ public class SearchQuerySpecDimExtractionFn extends DimExtractionFn
@JsonProperty("query") SearchQuerySpec searchQuerySpec
)
{
Preconditions.checkNotNull(searchQuerySpec, "search query must not be null");
this.searchQuerySpec = searchQuerySpec;
}
@ -74,4 +77,29 @@ public class SearchQuerySpecDimExtractionFn extends DimExtractionFn
"searchQuerySpec=" + searchQuerySpec +
'}';
}
@Override
public boolean equals(Object o)
{
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
SearchQuerySpecDimExtractionFn that = (SearchQuerySpecDimExtractionFn) o;
if (!searchQuerySpec.equals(that.searchQuerySpec)) {
return false;
}
return true;
}
@Override
public int hashCode()
{
return searchQuerySpec.hashCode();
}
}

View File

@ -19,6 +19,7 @@ package io.druid.query.extraction;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Preconditions;
import com.ibm.icu.text.SimpleDateFormat;
import com.metamx.common.StringUtils;
@ -43,6 +44,9 @@ public class TimeDimExtractionFn extends DimExtractionFn
@JsonProperty("resultFormat") String resultFormat
)
{
Preconditions.checkNotNull(timeFormat, "timeFormat must not be null");
Preconditions.checkNotNull(resultFormat, "resultFormat must not be null");
this.timeFormat = timeFormat;
this.timeFormatter = new SimpleDateFormat(timeFormat);
this.timeFormatter.setLenient(true);
@ -100,4 +104,34 @@ public class TimeDimExtractionFn extends DimExtractionFn
", resultFormat='" + resultFormat + '\'' +
'}';
}
@Override
public boolean equals(Object o)
{
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
TimeDimExtractionFn that = (TimeDimExtractionFn) o;
if (!resultFormat.equals(that.resultFormat)) {
return false;
}
if (!timeFormat.equals(that.timeFormat)) {
return false;
}
return true;
}
@Override
public int hashCode()
{
int result = timeFormat.hashCode();
result = 31 * result + resultFormat.hashCode();
return result;
}
}

View File

@ -110,4 +110,38 @@ public class TimeFormatExtractionFn implements ExtractionFn
{
return false;
}
@Override
public boolean equals(Object o)
{
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
TimeFormatExtractionFn that = (TimeFormatExtractionFn) o;
if (locale != null ? !locale.equals(that.locale) : that.locale != null) {
return false;
}
if (!pattern.equals(that.pattern)) {
return false;
}
if (tz != null ? !tz.equals(that.tz) : that.tz != null) {
return false;
}
return true;
}
@Override
public int hashCode()
{
int result = tz != null ? tz.hashCode() : 0;
result = 31 * result + pattern.hashCode();
result = 31 * result + (locale != null ? locale.hashCode() : 0);
return result;
}
}

View File

@ -0,0 +1,112 @@
/*
* Licensed to Metamarkets Group Inc. (Metamarkets) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Metamarkets licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package io.druid.query.dimension;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.druid.jackson.DefaultObjectMapper;
import io.druid.query.extraction.MatchingDimExtractionFn;
import io.druid.query.extraction.RegexDimExtractionFn;
import org.junit.Assert;
import org.junit.Test;
public class ExtractionDimensionSpecTest
{
@Test
public void testSerde() throws Exception
{
final ObjectMapper objectMapper = new DefaultObjectMapper();
final String oldJson = "{\n"
+ " \"type\": \"extraction\",\n"
+ " \"outputName\": \"first3Letters\",\n"
+ " \"dimension\": \"myDim\","
+ " \"extractionFn\": {\n"
+ " \"type\": \"regex\",\n"
+ " \"expr\": \"(...).*\"\n"
+ " }\n"
+ "}";
final ExtractionDimensionSpec extractionDimensionSpec = (ExtractionDimensionSpec) objectMapper.readValue(oldJson, DimensionSpec.class);
Assert.assertEquals("first3Letters", extractionDimensionSpec.getOutputName());
Assert.assertEquals("myDim", extractionDimensionSpec.getDimension());
Assert.assertNotNull(extractionDimensionSpec.getExtractionFn());
Assert.assertTrue(extractionDimensionSpec.getExtractionFn() instanceof RegexDimExtractionFn);
Assert.assertEquals(
extractionDimensionSpec,
objectMapper.readValue(
objectMapper.writeValueAsBytes(extractionDimensionSpec),
DimensionSpec.class
)
);
}
@Test
public void testSerdeBackwardsCompatibility() throws Exception
{
final ObjectMapper objectMapper = new DefaultObjectMapper();
final String oldJson = "{\n"
+ " \"type\": \"extraction\",\n"
+ " \"outputName\": \"first3Letters\",\n"
+ " \"dimension\": \"myDim\","
+ " \"dimExtractionFn\": {\n"
+ " \"type\": \"regex\",\n"
+ " \"expr\": \"(...).*\"\n"
+ " }\n"
+ "}";
final ExtractionDimensionSpec extractionDimensionSpec = (ExtractionDimensionSpec) objectMapper.readValue(oldJson, DimensionSpec.class);
Assert.assertEquals("first3Letters", extractionDimensionSpec.getOutputName());
Assert.assertEquals("myDim", extractionDimensionSpec.getDimension());
Assert.assertNotNull(extractionDimensionSpec.getExtractionFn());
Assert.assertTrue(extractionDimensionSpec.getExtractionFn() instanceof RegexDimExtractionFn);
Assert.assertEquals(
extractionDimensionSpec,
objectMapper.readValue(
objectMapper.writeValueAsBytes(extractionDimensionSpec),
DimensionSpec.class
)
);
// new trumps old
final String oldAndNewJson = "{\n"
+ " \"type\": \"extraction\",\n"
+ " \"outputName\": \"first3Letters\",\n"
+ " \"dimension\": \"myDim\","
+ " \"extractionFn\": {\n"
+ " \"type\": \"partial\",\n"
+ " \"expr\": \"(...).*\"\n"
+ " },\n"
+ " \"dimExtractionFn\": {\n"
+ " \"type\": \"regex\",\n"
+ " \"expr\": \"(...).*\"\n"
+ " }\n"
+ "}";
Assert.assertTrue(
objectMapper.readValue(oldAndNewJson, DimensionSpec.class)
.getExtractionFn() instanceof MatchingDimExtractionFn
);
}
}

View File

@ -87,12 +87,35 @@ public class TimeFormatExtractionFnTest
}
public void testSerde(TimeFormatExtractionFn fn, String format, DateTimeZone tz, String locale) throws Exception {
ObjectMapper objectMapper = new DefaultObjectMapper();
String json = objectMapper.writeValueAsString(fn);
final ObjectMapper objectMapper = new DefaultObjectMapper();
final String json = objectMapper.writeValueAsString(fn);
TimeFormatExtractionFn deserialized = objectMapper.readValue(json, TimeFormatExtractionFn.class);
Assert.assertEquals(format, deserialized.getFormat());
Assert.assertEquals(tz, deserialized.getTimeZone());
Assert.assertEquals(locale, deserialized.getLocale());
Assert.assertEquals(fn, deserialized);
}
@Test
public void testSerdeFromJson() throws Exception
{
final ObjectMapper objectMapper = new DefaultObjectMapper();
final String json = "{ \"type\" : \"timeFormat\", \"format\" : \"HH\" }";
TimeFormatExtractionFn extractionFn = (TimeFormatExtractionFn) objectMapper.readValue(json, ExtractionFn.class);
Assert.assertEquals("HH", extractionFn.getFormat());
Assert.assertEquals(null, extractionFn.getLocale());
Assert.assertEquals(null, extractionFn.getTimeZone());
// round trip
Assert.assertEquals(
extractionFn,
objectMapper.readValue(
objectMapper.writeValueAsBytes(extractionFn),
ExtractionFn.class
)
);
}
}

View File

@ -17,8 +17,10 @@
package io.druid.query.extraction.extraction;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import io.druid.jackson.DefaultObjectMapper;
import io.druid.query.extraction.ExtractionFn;
import io.druid.query.extraction.JavascriptExtractionFn;
import org.joda.time.DateTime;
@ -307,4 +309,23 @@ public class JavascriptExtractionFnTest
Assert.assertEquals(it.next(), res);
}
}
@Test
public void testSerde() throws Exception
{
final ObjectMapper objectMapper = new DefaultObjectMapper();
final String json = "{ \"type\" : \"javascript\", \"function\" : \"function(str) { return str.substring(0,3); }\" }";
JavascriptExtractionFn extractionFn = (JavascriptExtractionFn) objectMapper.readValue(json, ExtractionFn.class);
Assert.assertEquals("function(str) { return str.substring(0,3); }", extractionFn.getFunction());
// round trip
Assert.assertEquals(
extractionFn,
objectMapper.readValue(
objectMapper.writeValueAsBytes(extractionFn),
ExtractionFn.class
)
);
}
}

View File

@ -17,7 +17,9 @@
package io.druid.query.extraction.extraction;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Sets;
import io.druid.jackson.DefaultObjectMapper;
import io.druid.query.extraction.ExtractionFn;
import io.druid.query.extraction.MatchingDimExtractionFn;
import org.junit.Assert;
@ -64,4 +66,23 @@ public class MatchingDimExtractionFnTest
Assert.assertTrue(expected.contains(str));
}
}
@Test
public void testSerde() throws Exception
{
final ObjectMapper objectMapper = new DefaultObjectMapper();
final String json = "{ \"type\" : \"partial\", \"expr\" : \".(...)?\" }";
MatchingDimExtractionFn extractionFn = (MatchingDimExtractionFn) objectMapper.readValue(json, ExtractionFn.class);
Assert.assertEquals(".(...)?", extractionFn.getExpr());
// round trip
Assert.assertEquals(
extractionFn,
objectMapper.readValue(
objectMapper.writeValueAsBytes(extractionFn),
ExtractionFn.class
)
);
}
}

View File

@ -17,7 +17,9 @@
package io.druid.query.extraction.extraction;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Sets;
import io.druid.jackson.DefaultObjectMapper;
import io.druid.query.extraction.ExtractionFn;
import io.druid.query.extraction.RegexDimExtractionFn;
import org.junit.Assert;
@ -99,4 +101,23 @@ public class RegexDimExtractionFnTest
Assert.assertTrue(extracted.contains("b"));
Assert.assertTrue(extracted.contains("c"));
}
@Test
public void testSerde() throws Exception
{
final ObjectMapper objectMapper = new DefaultObjectMapper();
final String json = "{ \"type\" : \"regex\", \"expr\" : \".(...)?\" }";
RegexDimExtractionFn extractionFn = (RegexDimExtractionFn) objectMapper.readValue(json, ExtractionFn.class);
Assert.assertEquals(".(...)?", extractionFn.getExpr());
// round trip
Assert.assertEquals(
extractionFn,
objectMapper.readValue(
objectMapper.writeValueAsBytes(extractionFn),
ExtractionFn.class
)
);
}
}

View File

@ -17,8 +17,11 @@
package io.druid.query.extraction.extraction;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Sets;
import io.druid.jackson.DefaultObjectMapper;
import io.druid.query.extraction.ExtractionFn;
import io.druid.query.extraction.MatchingDimExtractionFn;
import io.druid.query.extraction.TimeDimExtractionFn;
import org.junit.Assert;
import org.junit.Test;
@ -70,4 +73,24 @@ public class TimeDimExtractionFnTest
Assert.assertTrue(quarters.contains("Q2/2012"));
Assert.assertTrue(quarters.contains("Q4/2012"));
}
@Test
public void testSerde() throws Exception
{
final ObjectMapper objectMapper = new DefaultObjectMapper();
final String json = "{ \"type\" : \"time\", \"timeFormat\" : \"MM/dd/yyyy\", \"resultFormat\" : \"QQQ/yyyy\" }";
TimeDimExtractionFn extractionFn = (TimeDimExtractionFn) objectMapper.readValue(json, ExtractionFn.class);
Assert.assertEquals("MM/dd/yyyy", extractionFn.getTimeFormat());
Assert.assertEquals("QQQ/yyyy", extractionFn.getResultFormat());
// round trip
Assert.assertEquals(
extractionFn,
objectMapper.readValue(
objectMapper.writeValueAsBytes(extractionFn),
ExtractionFn.class
)
);
}
}