feat: 支持循环数组输出扁平聚合
- 按输出参数保存并解析一层扁平聚合策略 - 覆盖解析、顺序聚合与异常类型校验
This commit is contained in:
@@ -38,6 +38,10 @@ public class Parameter implements Serializable, Cloneable {
|
|||||||
protected String value;
|
protected String value;
|
||||||
protected boolean required;
|
protected boolean required;
|
||||||
protected String defaultValue;
|
protected String defaultValue;
|
||||||
|
/**
|
||||||
|
* 是否在循环节点完成后将各轮数组输出合并一层。
|
||||||
|
*/
|
||||||
|
protected boolean flattenAggregation;
|
||||||
protected List<Parameter> children;
|
protected List<Parameter> children;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -167,6 +171,24 @@ public class Parameter implements Serializable, Cloneable {
|
|||||||
this.defaultValue = defaultValue;
|
this.defaultValue = defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断循环输出是否启用一层扁平聚合。
|
||||||
|
*
|
||||||
|
* @return 启用时返回 {@code true}
|
||||||
|
*/
|
||||||
|
public boolean isFlattenAggregation() {
|
||||||
|
return flattenAggregation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置循环输出的一层扁平聚合开关。
|
||||||
|
*
|
||||||
|
* @param flattenAggregation 是否启用
|
||||||
|
*/
|
||||||
|
public void setFlattenAggregation(boolean flattenAggregation) {
|
||||||
|
this.flattenAggregation = flattenAggregation;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isRequired() {
|
public boolean isRequired() {
|
||||||
return required;
|
return required;
|
||||||
}
|
}
|
||||||
@@ -273,6 +295,7 @@ public class Parameter implements Serializable, Cloneable {
|
|||||||
", value='" + value + '\'' +
|
", value='" + value + '\'' +
|
||||||
", required=" + required +
|
", required=" + required +
|
||||||
", defaultValue='" + defaultValue + '\'' +
|
", defaultValue='" + defaultValue + '\'' +
|
||||||
|
", flattenAggregation=" + flattenAggregation +
|
||||||
", children=" + children +
|
", children=" + children +
|
||||||
", enums=" + enums +
|
", enums=" + enums +
|
||||||
", formType='" + formType + '\'' +
|
", formType='" + formType + '\'' +
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ public final class LoopResultReference implements Serializable {
|
|||||||
private final String resultId;
|
private final String resultId;
|
||||||
private final int iterationCount;
|
private final int iterationCount;
|
||||||
private final String outputName;
|
private final String outputName;
|
||||||
|
private final boolean flattenAggregation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建循环结果引用。
|
* 创建循环结果引用。
|
||||||
@@ -24,23 +25,64 @@ public final class LoopResultReference implements Serializable {
|
|||||||
* @param outputName 输出名称
|
* @param outputName 输出名称
|
||||||
*/
|
*/
|
||||||
public LoopResultReference(String resultId, int iterationCount, String outputName) {
|
public LoopResultReference(String resultId, int iterationCount, String outputName) {
|
||||||
|
this(resultId, iterationCount, outputName, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建带聚合策略的循环结果引用。
|
||||||
|
*
|
||||||
|
* @param resultId 循环结果 ID
|
||||||
|
* @param iterationCount 迭代次数
|
||||||
|
* @param outputName 输出名称
|
||||||
|
* @param flattenAggregation 是否将各轮数组合并一层
|
||||||
|
*/
|
||||||
|
public LoopResultReference(
|
||||||
|
String resultId,
|
||||||
|
int iterationCount,
|
||||||
|
String outputName,
|
||||||
|
boolean flattenAggregation) {
|
||||||
this.resultId = Objects.requireNonNull(resultId, "resultId must not be null");
|
this.resultId = Objects.requireNonNull(resultId, "resultId must not be null");
|
||||||
this.iterationCount = iterationCount;
|
this.iterationCount = iterationCount;
|
||||||
this.outputName = Objects.requireNonNull(outputName, "outputName must not be null");
|
this.outputName = Objects.requireNonNull(outputName, "outputName must not be null");
|
||||||
|
this.flattenAggregation = flattenAggregation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取循环结果 ID。
|
||||||
|
*
|
||||||
|
* @return 循环结果 ID
|
||||||
|
*/
|
||||||
public String getResultId() {
|
public String getResultId() {
|
||||||
return resultId;
|
return resultId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取累计迭代次数。
|
||||||
|
*
|
||||||
|
* @return 累计迭代次数
|
||||||
|
*/
|
||||||
public int getIterationCount() {
|
public int getIterationCount() {
|
||||||
return iterationCount;
|
return iterationCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取输出名称。
|
||||||
|
*
|
||||||
|
* @return 输出名称
|
||||||
|
*/
|
||||||
public String getOutputName() {
|
public String getOutputName() {
|
||||||
return outputName;
|
return outputName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否将各轮数组输出合并一层。
|
||||||
|
*
|
||||||
|
* @return 启用时返回 {@code true}
|
||||||
|
*/
|
||||||
|
public boolean isFlattenAggregation() {
|
||||||
|
return flattenAggregation;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取跨异步审计边界使用的稳定引用类型。
|
* 获取跨异步审计边界使用的稳定引用类型。
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import java.util.Iterator;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -295,12 +296,38 @@ public interface LoopResultRepository {
|
|||||||
*/
|
*/
|
||||||
default Map<String, Object> references(
|
default Map<String, Object> references(
|
||||||
String resultId, int iterationCount, List<String> outputNames) {
|
String resultId, int iterationCount, List<String> outputNames) {
|
||||||
|
return references(
|
||||||
|
resultId,
|
||||||
|
iterationCount,
|
||||||
|
outputNames,
|
||||||
|
java.util.Collections.emptySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为每个循环输出创建带聚合策略的轻量引用。
|
||||||
|
*
|
||||||
|
* @param resultId 循环结果 ID
|
||||||
|
* @param iterationCount 已累计迭代数
|
||||||
|
* @param outputNames 输出名称
|
||||||
|
* @param flattenedOutputNames 启用一层扁平聚合的输出名称
|
||||||
|
* @return 输出名称到轻量引用的映射
|
||||||
|
*/
|
||||||
|
default Map<String, Object> references(
|
||||||
|
String resultId,
|
||||||
|
int iterationCount,
|
||||||
|
List<String> outputNames,
|
||||||
|
Set<String> flattenedOutputNames) {
|
||||||
Map<String, Object> references = new LinkedHashMap<>();
|
Map<String, Object> references = new LinkedHashMap<>();
|
||||||
if (outputNames != null) {
|
if (outputNames != null) {
|
||||||
for (String outputName : outputNames) {
|
for (String outputName : outputNames) {
|
||||||
references.put(
|
references.put(
|
||||||
outputName,
|
outputName,
|
||||||
new LoopResultReference(resultId, iterationCount, outputName));
|
new LoopResultReference(
|
||||||
|
resultId,
|
||||||
|
iterationCount,
|
||||||
|
outputName,
|
||||||
|
flattenedOutputNames != null
|
||||||
|
&& flattenedOutputNames.contains(outputName)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return references;
|
return references;
|
||||||
@@ -310,14 +337,15 @@ public interface LoopResultRepository {
|
|||||||
* 解析单个循环输出引用。
|
* 解析单个循环输出引用。
|
||||||
*
|
*
|
||||||
* @param reference 循环输出引用
|
* @param reference 循环输出引用
|
||||||
* @return 与旧实现相同的累计列表
|
* @return 未启用时返回按轮次累计的列表,启用时返回只扁平一层的数组
|
||||||
*/
|
*/
|
||||||
default Object resolve(LoopResultReference reference) {
|
default Object resolve(LoopResultReference reference) {
|
||||||
return load(
|
Object output = load(
|
||||||
reference.getResultId(),
|
reference.getResultId(),
|
||||||
reference.getIterationCount(),
|
reference.getIterationCount(),
|
||||||
List.of(reference.getOutputName()))
|
List.of(reference.getOutputName()))
|
||||||
.get(reference.getOutputName());
|
.get(reference.getOutputName());
|
||||||
|
return ReferenceResolver.applyAggregation(reference, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -420,7 +448,10 @@ public interface LoopResultRepository {
|
|||||||
LoopResultReference reference = (LoopResultReference) value;
|
LoopResultReference reference = (LoopResultReference) value;
|
||||||
Map<String, Object> outputs = loaded.get(new GroupKey(
|
Map<String, Object> outputs = loaded.get(new GroupKey(
|
||||||
reference.getResultId(), reference.getIterationCount()));
|
reference.getResultId(), reference.getIterationCount()));
|
||||||
return outputs == null ? null : outputs.get(reference.getOutputName());
|
Object output = outputs == null
|
||||||
|
? null
|
||||||
|
: outputs.get(reference.getOutputName());
|
||||||
|
return applyAggregation(reference, output);
|
||||||
}
|
}
|
||||||
if (value instanceof LoopInputReference) {
|
if (value instanceof LoopInputReference) {
|
||||||
return loadedInputs.get(
|
return loadedInputs.get(
|
||||||
@@ -451,6 +482,91 @@ public interface LoopResultRepository {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据循环结果引用对累计输出执行一次线性聚合。
|
||||||
|
*
|
||||||
|
* @param reference 循环结果引用
|
||||||
|
* @param output 按轮次累计的原始输出
|
||||||
|
* @return 原始输出或只扁平一层后的数组
|
||||||
|
* @throws IllegalStateException 启用扁平聚合但某轮值不是数组
|
||||||
|
*/
|
||||||
|
private static Object applyAggregation(
|
||||||
|
LoopResultReference reference, Object output) {
|
||||||
|
if (!reference.isFlattenAggregation()) {
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
if (!(output instanceof List<?>)) {
|
||||||
|
throw invalidFlattenValue(reference, -1, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<?> iterationValues = (List<?>) output;
|
||||||
|
int flattenedSize = 0;
|
||||||
|
int iterationIndex = 0;
|
||||||
|
// 使用顺序迭代兼容链表,避免按索引读取退化为 O(n²)。
|
||||||
|
for (Object iterationValue : iterationValues) {
|
||||||
|
int currentSize;
|
||||||
|
if (iterationValue instanceof List<?>) {
|
||||||
|
currentSize = ((List<?>) iterationValue).size();
|
||||||
|
} else if (iterationValue != null
|
||||||
|
&& iterationValue.getClass().isArray()) {
|
||||||
|
currentSize = java.lang.reflect.Array.getLength(iterationValue);
|
||||||
|
} else {
|
||||||
|
throw invalidFlattenValue(
|
||||||
|
reference, iterationIndex, iterationValue);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
flattenedSize = Math.addExact(flattenedSize, currentSize);
|
||||||
|
} catch (ArithmeticException exception) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Loop output '" + reference.getOutputName()
|
||||||
|
+ "' is too large to flatten",
|
||||||
|
exception);
|
||||||
|
}
|
||||||
|
iterationIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先统计容量再顺序追加,避免大数组扩容复制,整体复杂度保持 O(n)。
|
||||||
|
java.util.ArrayList<Object> flattened =
|
||||||
|
new java.util.ArrayList<>(flattenedSize);
|
||||||
|
for (Object iterationValue : iterationValues) {
|
||||||
|
if (iterationValue instanceof List<?>) {
|
||||||
|
flattened.addAll((List<?>) iterationValue);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int length = java.lang.reflect.Array.getLength(iterationValue);
|
||||||
|
for (int index = 0; index < length; index++) {
|
||||||
|
flattened.add(
|
||||||
|
java.lang.reflect.Array.get(iterationValue, index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return flattened;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造扁平聚合类型错误。
|
||||||
|
*
|
||||||
|
* @param reference 循环结果引用
|
||||||
|
* @param iterationIndex 错误轮次;负数表示累计输出结构错误
|
||||||
|
* @param value 实际值
|
||||||
|
* @return 类型错误
|
||||||
|
*/
|
||||||
|
private static IllegalStateException invalidFlattenValue(
|
||||||
|
LoopResultReference reference,
|
||||||
|
int iterationIndex,
|
||||||
|
Object value) {
|
||||||
|
String actualType = value == null
|
||||||
|
? "null"
|
||||||
|
: value.getClass().getName();
|
||||||
|
String iteration = iterationIndex < 0
|
||||||
|
? ""
|
||||||
|
: ", iteration " + iterationIndex;
|
||||||
|
return new IllegalStateException(
|
||||||
|
"Loop output '" + reference.getOutputName()
|
||||||
|
+ "' requires an array for flatten aggregation"
|
||||||
|
+ iteration
|
||||||
|
+ ", but got " + actualType);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 循环结果批量读取分组键。
|
* 循环结果批量读取分组键。
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -401,7 +401,10 @@ public class LoopNode extends BaseNode {
|
|||||||
prevTrigger.getLoopCursors().remove(this.id);
|
prevTrigger.getLoopCursors().remove(this.id);
|
||||||
}
|
}
|
||||||
Map<String, Object> completedResult = chain.getLoopResultRepository().references(
|
Map<String, Object> completedResult = chain.getLoopResultRepository().references(
|
||||||
loopContext.resultId, loopContext.currentIndex, getOutputNames());
|
loopContext.resultId,
|
||||||
|
loopContext.currentIndex,
|
||||||
|
getOutputNames(),
|
||||||
|
getFlattenedOutputNames());
|
||||||
chain.getLoopResultRepository().releaseActiveCache(
|
chain.getLoopResultRepository().releaseActiveCache(
|
||||||
loopContext.resultId);
|
loopContext.resultId);
|
||||||
if (!loopContext.inputExternalized) {
|
if (!loopContext.inputExternalized) {
|
||||||
@@ -1041,6 +1044,24 @@ public class LoopNode extends BaseNode {
|
|||||||
return outputNames;
|
return outputNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取启用一层扁平聚合的输出名称。
|
||||||
|
*
|
||||||
|
* @return 保持定义顺序的输出名称集合
|
||||||
|
*/
|
||||||
|
private Set<String> getFlattenedOutputNames() {
|
||||||
|
Set<String> outputNames = new LinkedHashSet<>();
|
||||||
|
List<Parameter> outputDefs = getOutputDefs();
|
||||||
|
if (outputDefs != null) {
|
||||||
|
for (Parameter outputDef : outputDefs) {
|
||||||
|
if (outputDef.isFlattenAggregation()) {
|
||||||
|
outputNames.add(outputDef.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return outputNames;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 估算本轮输出占用字节数,用于宽松的累计结果失控保护。
|
* 估算本轮输出占用字节数,用于宽松的累计结果失控保护。
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ public abstract class BaseNodeParser<T extends BaseNode> implements NodeParser<T
|
|||||||
parameter.setDataType(DataType.ofValue(parameterJsonObject.getString("dataType")));
|
parameter.setDataType(DataType.ofValue(parameterJsonObject.getString("dataType")));
|
||||||
parameter.setRequired(parameterJsonObject.getBooleanValue("required"));
|
parameter.setRequired(parameterJsonObject.getBooleanValue("required"));
|
||||||
parameter.setDefaultValue(parameterJsonObject.getString("defaultValue"));
|
parameter.setDefaultValue(parameterJsonObject.getString("defaultValue"));
|
||||||
|
parameter.setFlattenAggregation(
|
||||||
|
parameterJsonObject.getBooleanValue("flattenAggregation"));
|
||||||
|
|
||||||
//新增
|
//新增
|
||||||
parameter.setContentType(parameterJsonObject.getString("contentType"));
|
parameter.setContentType(parameterJsonObject.getString("contentType"));
|
||||||
|
|||||||
@@ -68,6 +68,36 @@ public class LoopNodeParserTest {
|
|||||||
Assert.assertNull(loopNode.getLoopItems());
|
Assert.assertNull(loopNode.getLoopItems());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证循环输出可以解析扁平聚合配置。
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void shouldParseFlattenAggregationFromOutputDefinition() {
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
com.alibaba.fastjson.JSONArray outputDefs =
|
||||||
|
new com.alibaba.fastjson.JSONArray();
|
||||||
|
JSONObject output = parameter(
|
||||||
|
"res",
|
||||||
|
"Array<String>",
|
||||||
|
"ref",
|
||||||
|
"knowledge.documents.content",
|
||||||
|
null);
|
||||||
|
output.put("flattenAggregation", true);
|
||||||
|
outputDefs.add(output);
|
||||||
|
data.put("outputDefs", outputDefs);
|
||||||
|
JSONObject nodeJson = new JSONObject();
|
||||||
|
nodeJson.put("id", "loop");
|
||||||
|
nodeJson.put("type", "loopNode");
|
||||||
|
nodeJson.put("data", data);
|
||||||
|
|
||||||
|
LoopNode loopNode = new LoopNodeParser().parse(
|
||||||
|
nodeJson, new JSONObject(), null);
|
||||||
|
|
||||||
|
Assert.assertEquals(1, loopNode.getOutputDefs().size());
|
||||||
|
Assert.assertTrue(
|
||||||
|
loopNode.getOutputDefs().get(0).isFlattenAggregation());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造参数 JSON。
|
* 构造参数 JSON。
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import java.util.LinkedHashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -42,6 +43,98 @@ public class LoopResultReferenceResolverTest {
|
|||||||
Assert.assertEquals(1, repository.getLoadCount());
|
Assert.assertEquals(1, repository.getLoadCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证数组输出按轮次和轮内顺序只扁平一层。
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void shouldFlattenArrayOutputOnceInStableOrder() {
|
||||||
|
InMemoryLoopResultRepository repository =
|
||||||
|
new InMemoryLoopResultRepository();
|
||||||
|
repository.append(
|
||||||
|
"result-flatten",
|
||||||
|
0,
|
||||||
|
Map.of(
|
||||||
|
"items",
|
||||||
|
List.of(
|
||||||
|
List.of("a"),
|
||||||
|
List.of("b"))));
|
||||||
|
repository.append(
|
||||||
|
"result-flatten",
|
||||||
|
1,
|
||||||
|
Map.of(
|
||||||
|
"items",
|
||||||
|
List.of(
|
||||||
|
List.of("c"))));
|
||||||
|
|
||||||
|
Object resolved = repository.resolve(
|
||||||
|
new LoopResultReference(
|
||||||
|
"result-flatten",
|
||||||
|
2,
|
||||||
|
"items",
|
||||||
|
true));
|
||||||
|
|
||||||
|
Assert.assertEquals(
|
||||||
|
List.of(
|
||||||
|
List.of("a"),
|
||||||
|
List.of("b"),
|
||||||
|
List.of("c")),
|
||||||
|
resolved);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证启用扁平聚合后拒绝某轮标量结果。
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void shouldRejectScalarIterationWhenFlattening() {
|
||||||
|
InMemoryLoopResultRepository repository =
|
||||||
|
new InMemoryLoopResultRepository();
|
||||||
|
repository.append(
|
||||||
|
"result-invalid",
|
||||||
|
0,
|
||||||
|
Map.of("items", List.of("a")));
|
||||||
|
repository.append(
|
||||||
|
"result-invalid",
|
||||||
|
1,
|
||||||
|
Map.of("items", "scalar"));
|
||||||
|
|
||||||
|
try {
|
||||||
|
repository.resolve(
|
||||||
|
new LoopResultReference(
|
||||||
|
"result-invalid",
|
||||||
|
2,
|
||||||
|
"items",
|
||||||
|
true));
|
||||||
|
Assert.fail("scalar iteration should be rejected");
|
||||||
|
} catch (IllegalStateException expected) {
|
||||||
|
Assert.assertTrue(
|
||||||
|
expected.getMessage().contains("items"));
|
||||||
|
Assert.assertTrue(
|
||||||
|
expected.getMessage().contains("iteration 1"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证循环完成时只给指定输出携带扁平聚合策略。
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void shouldCreateReferencesWithPerOutputAggregation() {
|
||||||
|
InMemoryLoopResultRepository repository =
|
||||||
|
new InMemoryLoopResultRepository();
|
||||||
|
|
||||||
|
Map<String, Object> references = repository.references(
|
||||||
|
"result-references",
|
||||||
|
2,
|
||||||
|
List.of("grouped", "flattened"),
|
||||||
|
Set.of("flattened"));
|
||||||
|
|
||||||
|
Assert.assertFalse(
|
||||||
|
((LoopResultReference) references.get("grouped"))
|
||||||
|
.isFlattenAggregation());
|
||||||
|
Assert.assertTrue(
|
||||||
|
((LoopResultReference) references.get("flattened"))
|
||||||
|
.isFlattenAggregation());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 验证无限 Iterable 在达到预算后立即停止物化。
|
* 验证无限 Iterable 在达到预算后立即停止物化。
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user