blob: 4d1bb347f2180732a3be0234b871fa0020bd2c42 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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 org.apache.druid.segment.nested;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.smile.SmileFactory;
import com.fasterxml.jackson.dataformat.smile.SmileGenerator;
import it.unimi.dsi.fastutil.Hash;
import org.apache.druid.data.input.impl.DimensionSchema;
import org.apache.druid.guice.NestedDataModule;
import org.apache.druid.jackson.DefaultObjectMapper;
import org.apache.druid.java.util.common.ISE;
import org.apache.druid.java.util.common.guava.Comparators;
import org.apache.druid.segment.DimensionHandler;
import org.apache.druid.segment.NestedDataColumnHandlerV4;
import org.apache.druid.segment.NestedDataColumnSchema;
import org.apache.druid.segment.column.ColumnBuilder;
import org.apache.druid.segment.column.ColumnCapabilities;
import org.apache.druid.segment.column.ColumnCapabilitiesImpl;
import org.apache.druid.segment.column.ColumnConfig;
import org.apache.druid.segment.column.ColumnFormat;
import org.apache.druid.segment.column.ColumnType;
import org.apache.druid.segment.column.ObjectStrategyComplexTypeStrategy;
import org.apache.druid.segment.column.TypeStrategy;
import org.apache.druid.segment.data.ObjectStrategy;
import org.apache.druid.segment.serde.ComplexMetricExtractor;
import org.apache.druid.segment.serde.ComplexMetricSerde;
import javax.annotation.Nullable;
import java.io.IOException;
import java.nio.ByteBuffer;
public class NestedDataComplexTypeSerde extends ComplexMetricSerde
{
public static final String TYPE_NAME = "json";
public static final ObjectMapper OBJECT_MAPPER;
public static final NestedDataComplexTypeSerde INSTANCE = new NestedDataComplexTypeSerde();
static {
final SmileFactory smileFactory = new SmileFactory();
smileFactory.configure(SmileGenerator.Feature.ENCODE_BINARY_AS_7BIT, false);
smileFactory.delegateToTextual(true);
final ObjectMapper mapper = new DefaultObjectMapper(smileFactory, null);
mapper.getFactory().setCodec(mapper);
mapper.registerModules(NestedDataModule.getJacksonModulesList());
OBJECT_MAPPER = mapper;
}
@Override
public String getTypeName()
{
return TYPE_NAME;
}
@Override
public ComplexMetricExtractor getExtractor()
{
throw new UnsupportedOperationException("Not supported");
}
@Override
public void deserializeColumn(ByteBuffer buffer, ColumnBuilder builder)
{
throw new UnsupportedOperationException("Not supported");
}
@Override
public void deserializeColumn(
ByteBuffer buffer,
ColumnBuilder builder,
ColumnConfig columnConfig
)
{
final NestedDataColumnSupplierV4 supplier = NestedDataColumnSupplierV4.read(
buffer,
builder,
columnConfig,
OBJECT_MAPPER
);
final ColumnCapabilitiesImpl capabilitiesBuilder = builder.getCapabilitiesBuilder();
capabilitiesBuilder.setDictionaryEncoded(true);
capabilitiesBuilder.setDictionaryValuesSorted(true);
capabilitiesBuilder.setDictionaryValuesUnique(true);
final ColumnType simpleType = supplier.getSimpleType();
if (simpleType != null) {
builder.setType(simpleType);
} else {
builder.setComplexTypeName(TYPE_NAME);
}
builder.setComplexColumnSupplier(supplier);
builder.setColumnFormat(new NestedColumnFormatV4());
}
@Override
public ObjectStrategy getObjectStrategy()
{
return new ObjectStrategy<Object>()
{
@Override
public int compare(Object o1, Object o2)
{
return Comparators.<StructuredData>naturalNullsFirst()
.compare(StructuredData.wrap(o1), StructuredData.wrap(o2));
}
@Override
public Class<? extends Object> getClazz()
{
return StructuredData.class;
}
@Nullable
@Override
public Object fromByteBuffer(ByteBuffer buffer, int numBytes)
{
final byte[] bytes = new byte[numBytes];
buffer.get(bytes, 0, numBytes);
try {
return OBJECT_MAPPER.readValue(bytes, StructuredData.class);
}
catch (IOException e) {
throw new ISE(e, "Unable to deserialize value");
}
}
@Nullable
@Override
public byte[] toBytes(@Nullable Object val)
{
if (val == null) {
return new byte[0];
}
try {
return OBJECT_MAPPER.writeValueAsBytes(val);
}
catch (JsonProcessingException e) {
throw new ISE(e, "Unable to serialize value [%s]", val);
}
}
};
}
@Override
public <T extends Comparable<T>> TypeStrategy<T> getTypeStrategy()
{
return new ObjectStrategyComplexTypeStrategy<>(
getObjectStrategy(),
ColumnType.ofComplex(TYPE_NAME),
new Hash.Strategy<Object>()
{
@Override
public int hashCode(Object o)
{
return StructuredData.wrap(o).equalityHash();
}
@Override
public boolean equals(Object a, Object b)
{
return StructuredData.wrap(a).compareTo(StructuredData.wrap(b)) == 0;
}
}
);
}
public static class NestedColumnFormatV4 implements ColumnFormat
{
@Override
public ColumnType getLogicalType()
{
return ColumnType.NESTED_DATA;
}
@Override
public DimensionHandler getColumnHandler(String columnName)
{
return new NestedDataColumnHandlerV4(columnName);
}
@Override
public DimensionSchema getColumnSchema(String columnName)
{
return new NestedDataColumnSchema(columnName, 4);
}
@Override
public ColumnFormat merge(@Nullable ColumnFormat otherFormat)
{
// we don't care, we are anything, there is no configurability
return this;
}
@Override
public ColumnCapabilities toColumnCapabilities()
{
return ColumnCapabilitiesImpl.createDefault().setType(ColumnType.NESTED_DATA).setHasNulls(true);
}
}
}