fix(reasoner): fix qlexpress keyword (#164)

Co-authored-by: FishJoy <chengqiang.cq@antgroup.com>
Co-authored-by: peilong <peilong.zpl@antgroup.com>
Co-authored-by: kejian <wangshaofei.wsf@antgroup.com>
This commit is contained in:
Donghai 2024-03-25 19:51:07 +08:00 committed by GitHub
parent 3f9fadb22c
commit 8c8074f92f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 146 additions and 15 deletions

View File

@ -0,0 +1,21 @@
/*
* Copyright 2023 OpenSPG Authors
*
* Licensed 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.
*/
package com.antgroup.openspg.reasoner.common.utils;
/**
* @author peilong.zpl
* @version $Id: FunctionUtils.java, v 0.1 2024-03-19 21:42 peilong.zpl Exp $$
*/
@FunctionalInterface
public interface JavaFunctionCaller<T, R> {
R apply(T t);
}

View File

@ -15,13 +15,21 @@ package com.antgroup.openspg.reasoner.lube.utils.transformer.impl
import com.antgroup.openspg.reasoner.common.trees.Transform
import com.antgroup.openspg.reasoner.common.types.KTString
import com.antgroup.openspg.reasoner.common.utils.JavaFunctionCaller
import com.antgroup.openspg.reasoner.lube.common.expr._
import com.antgroup.openspg.reasoner.lube.common.graph.IRNode
import com.antgroup.openspg.reasoner.lube.common.rule.Rule
import com.antgroup.openspg.reasoner.lube.utils.ExprUtils
import com.antgroup.openspg.reasoner.lube.utils.transformer.ExprTransformer
class Expr2QlexpressTransformer extends ExprTransformer[String] {
class Expr2QlexpressTransformer(
fieldNameTransFunc: JavaFunctionCaller[String, String]
= new JavaFunctionCaller[String, String] {
override def apply(t: String): String = t
}
)
extends ExprTransformer[String] {
val binaryOpSetTrans: PartialFunction[BinaryOpSet, String] = {
case BAdd => " + "
@ -50,7 +58,7 @@ class Expr2QlexpressTransformer extends ExprTransformer[String] {
case Abs => "abs(%s)"
case Floor => "floor(%s)"
case Ceil => "ceil(%s)"
case GetField(fieldName) => "%s." + fieldName
case GetField(fieldName) => "%s." + fieldNameTransFunc(fieldName)
}
def lambdaFuncParse(curVariableSet: Set[String], lambdaFunc: Expr): (String, String) = {

View File

@ -25,6 +25,7 @@ import com.antgroup.openspg.reasoner.lube.common.pattern.Pattern;
import com.antgroup.openspg.reasoner.lube.logical.PathVar;
import com.antgroup.openspg.reasoner.lube.logical.PropertyVar;
import com.antgroup.openspg.reasoner.lube.logical.Var;
import com.antgroup.openspg.reasoner.udf.rule.RuleRunner;
import com.antgroup.openspg.reasoner.utils.RunnerUtil;
import java.io.Serializable;
import java.util.List;
@ -93,13 +94,16 @@ public class SelectRowImpl implements Serializable {
private Object getSelectValue(String alias, String propertyName, Map<String, Object> context) {
if (Constants.PROPERTY_JSON_KEY.equals(propertyName)) {
Object propertyMap = context.get(alias);
Object propertyMap = RunnerUtil.recoverContextKeys(context.get(alias));
return JSON.toJSONString(
propertyMap,
SerializerFeature.PrettyFormat,
SerializerFeature.DisableCircularReferenceDetect,
SerializerFeature.SortField);
} else if (Constants.GET_PATH_KEY.equals(propertyName)) {
for (Map.Entry<String, Object> entry : context.entrySet()) {
RunnerUtil.recoverContextKeys(entry.getValue());
}
return JSON.toJSONString(
context,
SerializerFeature.PrettyFormat,
@ -107,6 +111,6 @@ public class SelectRowImpl implements Serializable {
SerializerFeature.SortField);
}
Map<String, Object> propertyMap = (Map<String, Object>) context.get(alias);
return propertyMap.get(propertyName);
return propertyMap.get(RuleRunner.convertPropertyName(propertyName));
}
}

View File

@ -91,6 +91,7 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import scala.Tuple2;
import scala.collection.JavaConversions;
@ -425,7 +426,7 @@ public class RunnerUtil {
IProperty property = vertex.getValue();
if (null != property) {
for (String key : property.getKeySet()) {
vertexProperty.put(key, property.get(key));
vertexProperty.put(RuleRunner.convertPropertyName(key), property.get(key));
}
}
vertexProperty.put(Constants.CONTEXT_LABEL, getVertexType(vertex));
@ -477,7 +478,7 @@ public class RunnerUtil {
IProperty property = edge.getValue();
if (null != property) {
for (String key : property.getKeySet()) {
edgeProperty.put(key, property.get(key));
edgeProperty.put(RuleRunner.convertPropertyName(key), property.get(key));
}
}
edgeProperty.put(Constants.CONTEXT_LABEL, edgeType);
@ -502,7 +503,7 @@ public class RunnerUtil {
IProperty property = edge.getValue();
if (null != property) {
for (String key : property.getKeySet()) {
edgeProperty.put(key, property.get(key));
edgeProperty.put(RuleRunner.convertPropertyName(key), property.get(key));
}
}
edgeProperty.put(Constants.REPEAT_EDGE_FLAG, true);
@ -540,7 +541,7 @@ public class RunnerUtil {
IProperty property = optionalEdge.getValue();
if (null != property) {
for (String key : property.getKeySet()) {
edgeProperty.put(key, property.get(key));
edgeProperty.put(RuleRunner.convertPropertyName(key), property.get(key));
}
}
edgeProperty.put(Constants.OPTIONAL_EDGE_FLAG, true);
@ -567,6 +568,31 @@ public class RunnerUtil {
return vertexProperty;
}
/** recover context keys */
public static Map<String, Object> recoverContextKeys(Map<String, Object> context) {
Map<String, Object> conflictKey = new HashMap<>();
Iterator<Map.Entry<String, Object>> it = context.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Object> entry = it.next();
if (!RuleRunner.isConflictPropertyName(entry.getKey())) {
continue;
}
it.remove();
conflictKey.put(RuleRunner.recoverPropertyName(entry.getKey()), entry.getValue());
}
if (MapUtils.isNotEmpty(conflictKey)) {
context.putAll(conflictKey);
}
return context;
}
public static Object recoverContextKeys(Object context) {
if (!(context instanceof Map)) {
return context;
}
return recoverContextKeys((Map<String, Object>) context);
}
public static final String FLATTEN_SEPARATOR = ".";
/**

View File

@ -163,7 +163,8 @@ public class RuleRunnerTest {
RuleExprParser parser = new RuleExprParser();
Expr expr = parser.parse("\"\"");
Expr2QlexpressTransformer transformer = new Expr2QlexpressTransformer();
Expr2QlexpressTransformer transformer =
new Expr2QlexpressTransformer(RuleRunner::convertPropertyName);
List<String> rules =
Lists.newArrayList(JavaConversions.asJavaCollection(transformer.transform(expr)));
Map<String, Object> context = new HashMap<>();
@ -402,7 +403,8 @@ public class RuleRunnerTest {
+ "and "
+ "not contains_any(s.status, [\"\", \"\", \"未见\"])");
Expr2QlexpressTransformer transformer = new Expr2QlexpressTransformer();
Expr2QlexpressTransformer transformer =
new Expr2QlexpressTransformer(RuleRunner::convertPropertyName);
List<String> rules =
Lists.newArrayList(JavaConversions.asJavaCollection(transformer.transform(e)));
@ -427,7 +429,8 @@ public class RuleRunnerTest {
RuleExprParser ruleExprParser = new RuleExprParser();
Expr e = ruleExprParser.parse("contains_any(s.entity, '包膜') and contains_any(s.entity, a)");
Expr2QlexpressTransformer transformer = new Expr2QlexpressTransformer();
Expr2QlexpressTransformer transformer =
new Expr2QlexpressTransformer(RuleRunner::convertPropertyName);
List<String> rules =
Lists.newArrayList(JavaConversions.asJavaCollection(transformer.transform(e)));
@ -447,6 +450,39 @@ public class RuleRunnerTest {
Assert.assertFalse(rst);
}
@Test
public void testKeywordConvert() {
RuleExprParser ruleExprParser = new RuleExprParser();
Expr e = ruleExprParser.parse("A.alias == B.alias && A.when == B.id");
Expr2QlexpressTransformer transformer =
new Expr2QlexpressTransformer(RuleRunner::convertPropertyName);
List<String> rules =
Lists.newArrayList(JavaConversions.asJavaCollection(transformer.transform(e)));
Map<String, Object> context = new HashMap<>();
context.put(
"A",
new HashMap<String, String>() {
{
put(RuleRunner.convertPropertyName("alias"), "alias_value");
put(RuleRunner.convertPropertyName("when"), "id_value");
}
});
context.put(
"B",
new HashMap<String, String>() {
{
put(RuleRunner.convertPropertyName("alias"), "alias_value");
put(RuleRunner.convertPropertyName("id"), "id_value");
}
});
boolean rst = RuleRunner.getInstance().check(context, rules, "");
Assert.assertTrue(rst);
}
private Map<String, Object> getRepeatTestContext() {
Map<String, Set<IVertex<IVertexId, IProperty>>> alias2VertexMap = new HashMap<>();
alias2VertexMap.put(
@ -535,7 +571,8 @@ public class RuleRunnerTest {
Assert.assertTrue(aliasSet.contains("A"));
Assert.assertTrue(aliasSet.contains("B"));
Assert.assertTrue(aliasSet.contains("e"));
Expr2QlexpressTransformer transformer = new Expr2QlexpressTransformer();
Expr2QlexpressTransformer transformer =
new Expr2QlexpressTransformer(RuleRunner::convertPropertyName);
List<String> rules =
Lists.newArrayList(JavaConversions.asJavaCollection(transformer.transform(e)));
Assert.assertEquals(
@ -550,7 +587,8 @@ public class RuleRunnerTest {
@Test
public void testRepeatReduce2() {
Expr e = ruleExprParser.parse("e.edges().reduce((pre, cur) => cur.rate * pre, 1)");
Expr2QlexpressTransformer transformer = new Expr2QlexpressTransformer();
Expr2QlexpressTransformer transformer =
new Expr2QlexpressTransformer(RuleRunner::convertPropertyName);
List<String> rules =
Lists.newArrayList(JavaConversions.asJavaCollection(transformer.transform(e)));
Assert.assertEquals(

View File

@ -31,9 +31,13 @@ import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import com.ql.util.express.Operator;
import com.ql.util.express.exception.QLCompileException;
import com.ql.util.express.parse.KeyWordDefine4Java;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -42,11 +46,39 @@ import scala.Tuple2;
public class RuleRunner {
private static final Logger log = LoggerFactory.getLogger(RuleRunner.class);
private static Cache<String, Map<String, Object>> contextCache =
private static final Cache<String, Map<String, Object>> contextCache =
CacheBuilder.newBuilder().maximumSize(100).expireAfterWrite(24, TimeUnit.HOURS).build();
private final ExpressRunner EXPRESS_RUNNER = new ExpressRunner();
private static final Set<String> keywordSet = new HashSet<>();
static {
KeyWordDefine4Java keyWordDefine4Java = new KeyWordDefine4Java();
keywordSet.addAll(Arrays.asList(keyWordDefine4Java.keyWords));
}
private static final String CONFLICT_KEY_PREFIX = "__ConflictKey_";
/** convert vertex or edge property name to prevent keyword conflicts */
public static String convertPropertyName(String propertyName) {
if (keywordSet.contains(propertyName)) {
return CONFLICT_KEY_PREFIX + propertyName;
}
return propertyName;
}
/** recover property name */
public static String recoverPropertyName(String propertyName) {
if (!propertyName.startsWith(CONFLICT_KEY_PREFIX)) {
return propertyName;
}
return propertyName.substring(CONFLICT_KEY_PREFIX.length());
}
public static boolean isConflictPropertyName(String propertyName) {
return propertyName.startsWith(CONFLICT_KEY_PREFIX);
}
/**
* set running context
*

View File

@ -22,6 +22,7 @@ import com.antgroup.openspg.reasoner.lube.common.rule.Rule;
import com.antgroup.openspg.reasoner.lube.utils.RuleUtils;
import com.antgroup.openspg.reasoner.lube.utils.transformer.ExprTransformer;
import com.antgroup.openspg.reasoner.lube.utils.transformer.impl.Expr2QlexpressTransformer;
import com.antgroup.openspg.reasoner.udf.rule.RuleRunner;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.HashMap;
@ -35,7 +36,8 @@ import scala.collection.immutable.Set;
public class WareHouseUtils {
private static final ExprTransformer<String> EXPR_TRANSFORMER = new Expr2QlexpressTransformer();
private static final ExprTransformer<String> EXPR_TRANSFORMER =
new Expr2QlexpressTransformer(RuleRunner::convertPropertyName);
/**
* get vertex rule string