/*
* 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.analyze.fetch;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import io.crate.analyze.relations.AnalyzedRelation;
import io.crate.analyze.relations.AnalyzedRelationVisitor;
import io.crate.analyze.relations.DocTableRelation;
import io.crate.analyze.symbol.DefaultTraversalSymbolVisitor;
import io.crate.analyze.symbol.Field;
import io.crate.analyze.symbol.Symbol;
import io.crate.metadata.Path;
import io.crate.metadata.doc.DocSysColumns;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class FetchFieldExtractor {
public static Set<Field> process(List<Symbol> symbols, Multimap<AnalyzedRelation, Symbol> subOutputs) {
HashSet<Field> canBeFetched = new HashSet<>();
Context ctx = new Context(subOutputs, canBeFetched);
for (Symbol symbol : symbols) {
FieldVisitor.INSTANCE.process(symbol, ctx);
}
return canBeFetched;
}
private static class Context {
private final Multimap<AnalyzedRelation, Symbol> outputs;
private final Collection<Field> canBeFetched;
private final Collection<Symbol> skipSymbols;
private Context(Multimap<AnalyzedRelation, Symbol> outputs, Collection<Field> canBeFetched) {
this.canBeFetched = canBeFetched;
this.skipSymbols = outputs.values();
this.outputs = outputs;
}
}
private static class FieldVisitor extends DefaultTraversalSymbolVisitor<Context, Boolean> {
private static final FieldVisitor INSTANCE = new FieldVisitor();
@Override
protected Boolean visitSymbol(Symbol symbol, Context context) {
return !context.skipSymbols.contains(symbol);
}
@Override
public Boolean visitField(Field field, Context context) {
if (context.skipSymbols.contains(field)) {
return false;
}
if (IsFetchableVisitor.isFetchable(field)) {
context.canBeFetched.add(field);
return true;
} else {
// if it is not fetchable, the field needs to be in the outputs of the according relation
context.outputs.put(field.relation(), field);
return false;
}
}
}
private static class IsFetchableVisitor extends AnalyzedRelationVisitor<Field, Boolean> {
private static final IsFetchableVisitor INSTANCE = new IsFetchableVisitor();
private static final Set<Path> NOT_FETCHABLE = ImmutableSet.<Path>of(DocSysColumns.SCORE, DocSysColumns.FETCHID);
public static Boolean isFetchable(Field field) {
return INSTANCE.process(field.relation(), field);
}
@Override
public Boolean visitDocTableRelation(DocTableRelation relation, Field field) {
return !NOT_FETCHABLE.contains(field.path());
}
@Override
protected Boolean visitAnalyzedRelation(AnalyzedRelation relation, Field context) {
return false;
}
}
}