Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.ssssssss.script.runtime.linq.LinQBuilder Maven / Gradle / Ivy
package org.ssssssss.script.runtime.linq;
import org.ssssssss.script.functions.MapExtension;
import org.ssssssss.script.functions.ObjectConvertExtension;
import org.ssssssss.script.functions.StreamExtension;
import org.ssssssss.script.runtime.Variables;
import org.ssssssss.script.runtime.function.MagicScriptLambdaFunction;
import org.ssssssss.script.runtime.handle.OperatorHandle;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class LinQBuilder {
private final Variables variables;
private static final Object[] EMPTY_PARAMETER = new Object[0];
private List fromObjects;
private int fromAliasIndex;
private final List selects = new ArrayList<>();
private MagicScriptLambdaFunction where;
private MagicScriptLambdaFunction having;
private final List groups = new ArrayList<>();
private final List joins = new ArrayList<>();
private int joinSize = 0;
private final List orders = new ArrayList<>();
private long limit = Long.MIN_VALUE;
private long offset = Long.MIN_VALUE;
private LinQBuilder(Variables variables) {
this.variables = variables;
}
public static LinQBuilder create(Variables variables) {
return new LinQBuilder(variables);
}
public LinQBuilder where(MagicScriptLambdaFunction condition) {
where = condition;
return this;
}
public LinQBuilder from(Object object, int aliasIndex) {
this.fromAliasIndex = aliasIndex;
this.fromObjects = convertToList(object);
return this;
}
private List convertToList(Object object) {
if (object instanceof Map) {
return (List) MapExtension.asList((Map, ?>) object, (entry) -> Collections.singletonMap(entry[0], entry[1]));
} else {
try {
return StreamExtension.arrayLikeToList(object);
} catch (Exception e) {
return Collections.singletonList(object);
}
}
}
public LinQBuilder group(MagicScriptLambdaFunction function) {
groups.add(function);
return this;
}
public LinQBuilder join(MagicScriptLambdaFunction condition, Object object, boolean isLeftJoin, int aliasIndex) {
joins.add(new LinQJoinValue(condition, convertToList(object), isLeftJoin, aliasIndex));
joinSize++;
return this;
}
public LinQBuilder having(MagicScriptLambdaFunction condition) {
having = condition;
return this;
}
public LinQBuilder order(MagicScriptLambdaFunction function, int order) {
this.orders.add(new LinQOrder(function, order));
return this;
}
public LinQBuilder select(MagicScriptLambdaFunction function, String aliasName, int aliasIndex) {
selects.add(new SelectField(function, aliasName, aliasIndex));
return this;
}
public Object executeAndFetchFirst() {
List> list = execute();
return list.isEmpty() ? null : list.get(0);
}
public LinQBuilder limit(Object limit) {
this.limit = ObjectConvertExtension.asLong(limit, Long.MIN_VALUE);
return this;
}
public LinQBuilder offset(Object offset) {
this.offset = ObjectConvertExtension.asLong(offset, Long.MIN_VALUE);
return this;
}
public List> execute() {
List records = new ArrayList<>();
for (Object object : fromObjects) {
variables.setValue(fromAliasIndex, object);
// 处理 join + where
records.addAll(processWhere(processJoin(object), object));
}
// 处理 group + having
records = processGroup(records);
// 处理 select
List result = processSelect(records);
Stream> stream = result.stream().sorted().map(SelectValue::getValue);
if (offset > 0) {
stream = stream.skip(offset);
}
if (limit > 0) {
stream = stream.limit(limit);
}
return stream.collect(Collectors.toList());
}
private void processRow(Object item, Map row, SelectField field) {
if (item instanceof Map) {
row.putAll((Map) item);
} else if (field.isWhole() && item instanceof List) {
row.putAll((Map) StreamExtension.first(item));
} else {
row.put(field.getAliasName(), item);
}
}
private List processSelect(List records) {
List result = new ArrayList<>(records.size());
int fieldSize = selects.size();
for (Record record : records) {
Map row = new LinkedHashMap<>(fieldSize);
variables.setValue(fromAliasIndex, record.getValue());
record.setVariableValue(variables);
for (SelectField field : selects) {
MagicScriptLambdaFunction function = field.getFunction();
if (function == null) {
processRow(record.getValue(), row, field);
record.getJoinValues().forEach(item -> processRow(item, row, field));
} else {
processRow(apply(function, variables, EMPTY_PARAMETER), row, field);
}
}
List orderValues = new ArrayList<>();
if (!orders.isEmpty()) {
for (LinQOrder order : orders) {
orderValues.add(new OrderValue(apply(order.getFunction(), variables, EMPTY_PARAMETER), order.getOrder()));
}
}
result.add(new SelectValue(row, orderValues));
record.removeVariableValue(variables);
}
return result;
}
private List processGroup(List records) {
if (!groups.isEmpty()) {
Map, List> group = new LinkedHashMap<>();
for (Record record : records) {
variables.setValue(fromAliasIndex, record.getValue());
record.setVariableValue(variables);
List keys = groups.stream().map(field -> apply(field, variables, EMPTY_PARAMETER)).collect(Collectors.toList());
group.computeIfAbsent(keys, k -> new ArrayList<>()).add(record);
}
records = new ArrayList<>();
for (Map.Entry, List> entry : group.entrySet()) {
List value = entry.getValue();
Map> joinMap = new HashMap<>();
List joinValues = new ArrayList<>();
value.stream()
.map(Record::getJoinValues)
.flatMap(Collection::stream)
.forEach(item -> joinMap.computeIfAbsent(item.getIndex(), k -> new ArrayList<>()).add(item.getValue()));
joinMap.forEach((key, joinValue) -> joinValues.add(new JoinValue(key, joinValue)));
Record record = new Record(value.stream().map(Record::getValue).collect(Collectors.toList()), joinValues);
boolean valid = true;
if (having != null) {
variables.setValue(fromAliasIndex, record.getValue());
record.setVariableValue(variables);
valid = OperatorHandle.isTrue(apply(having, variables, EMPTY_PARAMETER));
}
if (valid) {
records.add(record);
}
}
}
return records;
}
private List processWhere(List records, Object object) {
if (where != null) {
variables.setValue(fromAliasIndex, object);
return records.stream()
.peek(record -> record.setVariableValue(variables))
.filter(record -> OperatorHandle.isTrue(apply(where, variables, EMPTY_PARAMETER)))
.collect(Collectors.toList());
}
return records;
}
private Object apply(MagicScriptLambdaFunction function, Variables variables, Object[] args) {
return function.apply(variables, args);
}
private boolean processOtherJoin(int index, Map> container) {
if (index < joinSize) {
LinQJoinValue join = joins.get(index);
boolean matched = false;
for (Object joinItem : join.getTarget()) {
// 设置当前关联表所在行的数据
variables.setValue(join.getAliasIndex(), joinItem);
// 判断关联条件是否成立
if (OperatorHandle.isTrue(apply(join.getCondition(), variables, EMPTY_PARAMETER))) {
matched = true;
if (processOtherJoin(index + 1, container)) {
// join关联不上时直接跳过当前行
continue;
}
// 关联上时,把当前结果存放至容器中
container.get(join.getAliasIndex()).add(joinItem);
}
}
variables.setValue(join.getAliasIndex(), Collections.emptyMap());
if(!matched){
if(!join.isLeftJoin()){
return true;
}
return processOtherJoin(index + 1, container);
}
}
return false;
}
private List processJoin(Object object) {
// 当没有join语句时,直接返回该行数据
if (joinSize == 0) {
return Collections.singletonList(new Record(object));
}
// 定义存放关联结果集的容器并初始化
Map> map = new HashMap<>(joinSize);
joins.forEach(join -> map.put(join.getAliasIndex(), new ArrayList<>()));
if(processOtherJoin(0, map)){
return Collections.emptyList();
}
// 计算当前行关联到的行数
int maxSize = map.values().stream().mapToInt(Collection::size).max().getAsInt();
// 全没关联上,且没有 join
if (maxSize == 0) {
return Collections.singletonList(new Record(object, map.keySet().stream().map(objects -> new JoinValue(objects, Collections.emptyMap())).collect(Collectors.toList())));
}
List records = new ArrayList<>(maxSize);
Set>> entries = map.entrySet();
for (int i = 0; i < maxSize; i++) {
List joinValues = new ArrayList<>(entries.size());
for (Map.Entry> entry : entries) {
List item = entry.getValue();
int size = item.size();
if (size > 0) {
if (size > i) {
joinValues.add(new JoinValue(entry.getKey(), item.get(i)));
} else {
joinValues.add(new JoinValue(entry.getKey(), Collections.emptyMap()));
}
}
}
records.add(new Record(object, joinValues));
}
return records;
}
}