/*
Copyright 2013 Red Hat, Inc. and/or its affiliates.
This file is part of lightblue.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.redhat.lightblue.mindex;
import java.util.List;
import java.util.ArrayList;
import java.util.stream.Collectors;
import com.redhat.lightblue.assoc.QueryFieldInfo;
import com.redhat.lightblue.metadata.ArrayField;
import com.redhat.lightblue.query.*;
import com.redhat.lightblue.util.Path;
import com.redhat.lightblue.util.Error;
/**
* Given a query and key spec, builds a lookup spec
*
* This class follows the same pattern as GetIndexKeySpec
*/
public class GetIndexLookupSpec extends IndexQueryProcessorBase<LookupSpec> {
private static final String REGEX_CHARS="$[]*.\\^-&|{}()?+:<>!=";
public GetIndexLookupSpec(List<QueryFieldInfo> l) {
super(l);
}
@Override
protected LookupSpec processValueComparisonExpression(ValueComparisonExpression q) {
switch(q.getOp()) {
case _eq:
return new ValueLookupSpec(simpleKeySpec(findFieldInfo(q.getField(),q)),q.getRvalue());
case _lte:
case _lt:
return new RangeLookupSpec(simpleKeySpec(findFieldInfo(q.getField(),q)),null,q.getRvalue());
case _gte:
case _gt:
return new RangeLookupSpec(simpleKeySpec(findFieldInfo(q.getField(),q)),q.getRvalue(),null);
}
return null;
}
static String getPrefix(String pattern) {
StringBuilder bld=new StringBuilder();
int n=pattern.length();
for(int i=0;i<n;i++) {
char c=pattern.charAt(i);
if(i==0) {
if(c=='^') {
// ok
} else if(pattern.startsWith("\\A")) {
// ok
i++; // pass A
} else {
break;
}
} else {
if(REGEX_CHARS.indexOf(c)!=-1)
bld.append(c);
else
break;
}
}
return bld.toString();
}
@Override
protected LookupSpec processRegexMatchExpression(RegexMatchExpression q) {
return new PrefixLookupSpec(simpleKeySpec(findFieldInfo(q.getField(),q)),getPrefix(q.getRegex()),q.isCaseInsensitive());
}
@Override
protected LookupSpec processInExpression(NaryValueRelationalExpression q) {
return new MultiValueLookupSpec(simpleKeySpec(findFieldInfo(q.getField(),q)),
q.getValues().stream().map(Value::getValue).collect(Collectors.toList()));
}
@Override
protected LookupSpec processAnyExpression(ArrayContainsExpression q) {
return new MultiValueLookupSpec(simpleKeySpec(findFieldInfo(q.getArray(),q)),
q.getValues().stream().map(Value::getValue).collect(Collectors.toList()));
}
@Override
protected LookupSpec processOrQueries(List<QueryExpression> list,Path context) {
// Here, we're sure that all queries in the list refer to the same field, and there is only one
// SimpleKeySpec for the whole list
// Thus, we build a multiValuelookupSpec
List<Object> values=new ArrayList<>(list.size());
QueryExpression firstq=null;
Path firstPath=null;
boolean first=true;
for(QueryExpression q:list) {
if(first)
firstq=q;
if(q instanceof ValueComparisonExpression) {
if(first)
firstPath=((ValueComparisonExpression)q).getField();
values.add( ((ValueComparisonExpression)q).getRvalue().getValue() );
} else if(q instanceof NaryValueRelationalExpression) {
if(first)
firstPath=((NaryValueRelationalExpression)q).getField();
for(Value v: ((NaryValueRelationalExpression)q).getValues()) {
values.add(v.getValue());
}
}
first=false;
}
return new MultiValueLookupSpec(simpleKeySpec(findFieldInfo(firstPath,firstq)),values);
}
@Override
protected LookupSpec processAndQueries(List<QueryExpression> list,Path context) {
List<LookupSpec> specs=new ArrayList<>(list.size());
for(QueryExpression query:list) {
LookupSpec spec=super.iterate(query,context);
if(spec!=null)
specs.add(spec);
}
if(specs.isEmpty()) {
return null;
} else {
return new CompositeLookupSpec(specs.toArray(new LookupSpec[specs.size()]));
}
}
@Override
protected LookupSpec processArrayMatchExpression(QueryExpression nestedExpression,Path nestedContext) {
LookupSpec nestedSpec=iterate(nestedExpression,nestedContext);
if(nestedSpec==null) {
return null;
} else {
if(nestedSpec instanceof CompositeLookupSpec) {
return new ArrayLookupSpec(((CompositeLookupSpec)nestedSpec).values);
} else {
return new ArrayLookupSpec(new LookupSpec[]{nestedSpec});
}
}
}
}