/* * Licensed to Crate under one or more contributor license agreements. * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. Crate 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. * * However, if you have executed another commercial license agreement * with Crate these terms will supersede the license and you may use the * software solely pursuant to the terms of the relevant commercial * agreement. */ package io.crate.operation.scalar; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import io.crate.analyze.symbol.Symbol; import io.crate.metadata.*; import io.crate.data.Input; import io.crate.types.ArrayType; import io.crate.types.DataType; import io.crate.types.DataTypes; import org.elasticsearch.common.Nullable; import java.util.*; class ArrayDifferenceFunction extends Scalar<Object[], Object> { public static final String NAME = "array_difference"; private FunctionInfo functionInfo; private final Optional<Set<Object>> optionalSubtractSet; private static FunctionInfo createInfo(List<DataType> types) { ArrayType arrayType = (ArrayType) types.get(0); if (arrayType.innerType().equals(DataTypes.UNDEFINED)) { arrayType = (ArrayType) types.get(1); } return new FunctionInfo(new FunctionIdent(NAME, types), arrayType); } public static void register(ScalarFunctionModule module) { module.register(NAME, new Resolver()); } private ArrayDifferenceFunction(FunctionInfo functionInfo, @Nullable Set<Object> subtractSet) { this.functionInfo = functionInfo; optionalSubtractSet = Optional.fromNullable(subtractSet); } @Override public FunctionInfo info() { return functionInfo; } @Override public Scalar<Object[], Object> compile(List<Symbol> arguments) { Symbol symbol = arguments.get(1); if (!symbol.symbolType().isValueSymbol()) { // arguments are no values, we can't compile return this; } Input input = (Input) symbol; Object inputValue = input.value(); DataType innerType = ((ArrayType) this.info().returnType()).innerType(); Object[] array = (Object[]) inputValue; Set<Object> subtractSet = new HashSet<>(); if (array.length > 0) { for (Object element : array) { subtractSet.add(innerType.value(element)); } } return new ArrayDifferenceFunction(this.functionInfo, subtractSet); } @Override public Object[] evaluate(Input[] args) { Object[] originalArray = (Object[]) args[0].value(); if (originalArray == null) { return null; } DataType innerType = ((ArrayType) this.info().returnType()).innerType(); Set<Object> localSubtractSet; if (!optionalSubtractSet.isPresent()) { localSubtractSet = new HashSet<>(); for (int i = 1; i < args.length; i++) { Object argValue = args[i].value(); if (argValue == null) { continue; } Object[] array = (Object[]) argValue; for (Object element : array) { localSubtractSet.add(innerType.value(element)); } } } else { localSubtractSet = optionalSubtractSet.get(); } List<Object> resultList = new ArrayList<>(originalArray.length); for (Object anOriginalArray : originalArray) { Object element = innerType.value(anOriginalArray); if (!localSubtractSet.contains(element)) { resultList.add(element); } } return resultList.toArray(); } private static class Resolver implements FunctionResolver { private static final Signature.SignatureOperator SIGNATURE = Signature.of(Signature.ArgMatcher.ANY_ARRAY, Signature.ArgMatcher.ANY_ARRAY); @Override public FunctionImplementation getForTypes(List<DataType> dataTypes) throws IllegalArgumentException { for (int i = 0; i < dataTypes.size(); i++) { Preconditions.checkArgument(dataTypes.get(i) instanceof ArrayType, String.format(Locale.ENGLISH, "Argument %d of the array_difference function is not an array type", i + 1)); } DataType innerType0 = ((ArrayType) dataTypes.get(0)).innerType(); DataType innerType1 = ((ArrayType) dataTypes.get(1)).innerType(); Preconditions.checkArgument( !innerType0.equals(DataTypes.UNDEFINED) || !innerType1.equals(DataTypes.UNDEFINED), "One of the arguments of the array_difference function can be of undefined inner type, but not both"); if (!innerType0.equals(DataTypes.UNDEFINED)) { Preconditions.checkArgument(innerType1.isConvertableTo(innerType0), String.format(Locale.ENGLISH, "Second argument's inner type (%s) of the array_difference function cannot be converted to the first argument's inner type (%s)", innerType1, innerType0)); } return new ArrayDifferenceFunction(createInfo(dataTypes), null); } @javax.annotation.Nullable @Override public List<DataType> getSignature(List<DataType> dataTypes) { return SIGNATURE.apply(dataTypes); } } }