diff --git a/reasoner/common/src/main/java/com/antgroup/openspg/reasoner/common/utils/JavaFunctionCaller.java b/reasoner/common/src/main/java/com/antgroup/openspg/reasoner/common/utils/JavaFunctionCaller.java new file mode 100644 index 00000000..b4f4e861 --- /dev/null +++ b/reasoner/common/src/main/java/com/antgroup/openspg/reasoner/common/utils/JavaFunctionCaller.java @@ -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 { + R apply(T t); +} diff --git a/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/utils/transformer/impl/Expr2QlexpressTransformer.scala b/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/utils/transformer/impl/Expr2QlexpressTransformer.scala index 8264cdd2..d67e1990 100644 --- a/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/utils/transformer/impl/Expr2QlexpressTransformer.scala +++ b/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/utils/transformer/impl/Expr2QlexpressTransformer.scala @@ -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) = { diff --git a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/rdg/common/SelectRowImpl.java b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/rdg/common/SelectRowImpl.java index 47caf351..0088a69e 100644 --- a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/rdg/common/SelectRowImpl.java +++ b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/rdg/common/SelectRowImpl.java @@ -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 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 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 propertyMap = (Map) context.get(alias); - return propertyMap.get(propertyName); + return propertyMap.get(RuleRunner.convertPropertyName(propertyName)); } } diff --git a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/utils/RunnerUtil.java b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/utils/RunnerUtil.java index 6d956918..b96983ff 100644 --- a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/utils/RunnerUtil.java +++ b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/utils/RunnerUtil.java @@ -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 recoverContextKeys(Map context) { + Map conflictKey = new HashMap<>(); + Iterator> it = context.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry 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) context); + } + public static final String FLATTEN_SEPARATOR = "."; /** diff --git a/reasoner/runner/runner-common/src/test/java/com/antgroup/openspg/reasoner/rule/RuleRunnerTest.java b/reasoner/runner/runner-common/src/test/java/com/antgroup/openspg/reasoner/rule/RuleRunnerTest.java index 4a0d7df3..4e4bd402 100644 --- a/reasoner/runner/runner-common/src/test/java/com/antgroup/openspg/reasoner/rule/RuleRunnerTest.java +++ b/reasoner/runner/runner-common/src/test/java/com/antgroup/openspg/reasoner/rule/RuleRunnerTest.java @@ -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 rules = Lists.newArrayList(JavaConversions.asJavaCollection(transformer.transform(expr))); Map 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 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 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 rules = + Lists.newArrayList(JavaConversions.asJavaCollection(transformer.transform(e))); + Map context = new HashMap<>(); + context.put( + "A", + new HashMap() { + { + put(RuleRunner.convertPropertyName("alias"), "alias_value"); + put(RuleRunner.convertPropertyName("when"), "id_value"); + } + }); + + context.put( + "B", + new HashMap() { + { + put(RuleRunner.convertPropertyName("alias"), "alias_value"); + put(RuleRunner.convertPropertyName("id"), "id_value"); + } + }); + + boolean rst = RuleRunner.getInstance().check(context, rules, ""); + Assert.assertTrue(rst); + } + private Map getRepeatTestContext() { Map>> 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 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 rules = Lists.newArrayList(JavaConversions.asJavaCollection(transformer.transform(e))); Assert.assertEquals( diff --git a/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/rule/RuleRunner.java b/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/rule/RuleRunner.java index e6fa5104..b25312a8 100644 --- a/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/rule/RuleRunner.java +++ b/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/rule/RuleRunner.java @@ -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> contextCache = + private static final Cache> contextCache = CacheBuilder.newBuilder().maximumSize(100).expireAfterWrite(24, TimeUnit.HOURS).build(); private final ExpressRunner EXPRESS_RUNNER = new ExpressRunner(); + private static final Set 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 * diff --git a/reasoner/warehouse/warehouse-common/src/main/java/com/antgroup/openspg/reasoner/warehouse/utils/WareHouseUtils.java b/reasoner/warehouse/warehouse-common/src/main/java/com/antgroup/openspg/reasoner/warehouse/utils/WareHouseUtils.java index d0550bf0..5082209e 100644 --- a/reasoner/warehouse/warehouse-common/src/main/java/com/antgroup/openspg/reasoner/warehouse/utils/WareHouseUtils.java +++ b/reasoner/warehouse/warehouse-common/src/main/java/com/antgroup/openspg/reasoner/warehouse/utils/WareHouseUtils.java @@ -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 EXPR_TRANSFORMER = new Expr2QlexpressTransformer(); + private static final ExprTransformer EXPR_TRANSFORMER = + new Expr2QlexpressTransformer(RuleRunner::convertPropertyName); /** * get vertex rule string