From bbcfa53a78d188ff318e98926eaad9a6d67bfa45 Mon Sep 17 00:00:00 2001 From: matthewhyx Date: Fri, 22 Mar 2024 14:15:29 +0800 Subject: [PATCH 01/29] feat(schema): fix schema preload bug --- python/knext/knext/schema/marklang/schema_ml.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/knext/knext/schema/marklang/schema_ml.py b/python/knext/knext/schema/marklang/schema_ml.py index 9298dca73..1cc598835 100644 --- a/python/knext/knext/schema/marklang/schema_ml.py +++ b/python/knext/knext/schema/marklang/schema_ml.py @@ -936,7 +936,7 @@ def preload_types(self, lines: list): r"^([a-zA-Z0-9]+)\((\w+)\)\s*?->\s*?([a-zA-Z0-9\.]+):$", line ) if sub_type_match: - self.defined_types[sub_type_match.group(1)] = type_match.group( + self.defined_types[sub_type_match.group(1)] = sub_type_match.group( 3 ).strip() From 04d80f29e677ca6c144f4dea5fa996b1ab2db50e Mon Sep 17 00:00:00 2001 From: matthewhyx Date: Thu, 9 May 2024 11:10:14 +0800 Subject: [PATCH 02/29] feat(schema): support defining reasoning rule --- .../builder/core/reason/ReasonProcessor.java | 4 +- .../reason/impl/CausalConceptReasoner.java | 12 +- .../schema/supplychain_schema_helper.py | 174 ++++++++++-------- .../knext/schema/marklang/concept_rule_ml.py | 99 +++++++++- .../define_logical_causation_request.py | 14 ++ .../remove_logical_causation_request.py | 14 ++ .../server/api/facade/SchemaJsonUtils.java | 4 +- .../api/facade/client/ConceptFacade.java | 8 +- .../api/http/client/HttpConceptFacade.java | 8 +- .../forest/client/ConceptForestClient.java | 8 +- .../server/openapi/ConceptController.java | 8 +- .../server/biz/schema/ConceptManager.java | 8 +- .../biz/schema/impl/ConceptManagerImpl.java | 39 ++-- .../model/semantic/SPGOntologyEnum.java | 7 +- ...ationSemantic.java => TripleSemantic.java} | 29 ++- ....java => DefineTripleSemanticRequest.java} | 15 +- ....java => RemoveTripleSemanticRequest.java} | 17 +- .../core/schema/model/type/Concept.java | 8 +- .../core/schema/model/type/ConceptList.java | 10 +- .../concept/ConceptSemanticService.java | 21 ++- .../convertor/ConceptSemanticConvertor.java | 14 +- .../impl/ConceptSemanticServiceImpl.java | 24 +-- .../model/ReasoningConclusionQuery.java | 35 ++++ ...ionQuery.java => TripleSemanticQuery.java} | 9 +- .../repository/SemanticRepository.java | 8 +- .../schema/SemanticRepositoryImpl.java | 14 +- 26 files changed, 432 insertions(+), 179 deletions(-) rename server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/{LogicalCausationSemantic.java => TripleSemantic.java} (73%) rename server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/request/{DefineLogicalCausationRequest.java => DefineTripleSemanticRequest.java} (88%) rename server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/request/{RemoveLogicalCausationRequest.java => RemoveTripleSemanticRequest.java} (84%) create mode 100644 server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/semantic/model/ReasoningConclusionQuery.java rename server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/semantic/model/{LogicalCausationQuery.java => TripleSemanticQuery.java} (84%) diff --git a/builder/core/src/main/java/com/antgroup/openspg/builder/core/reason/ReasonProcessor.java b/builder/core/src/main/java/com/antgroup/openspg/builder/core/reason/ReasonProcessor.java index cd4ba35c5..508361085 100644 --- a/builder/core/src/main/java/com/antgroup/openspg/builder/core/reason/ReasonProcessor.java +++ b/builder/core/src/main/java/com/antgroup/openspg/builder/core/reason/ReasonProcessor.java @@ -26,7 +26,7 @@ import com.antgroup.openspg.builder.model.record.BaseRecord; import com.antgroup.openspg.builder.model.record.BaseSPGRecord; import com.antgroup.openspg.core.schema.model.semantic.DynamicTaxonomySemantic; -import com.antgroup.openspg.core.schema.model.semantic.LogicalCausationSemantic; +import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.core.schema.model.type.ConceptList; import com.antgroup.openspg.reasoner.catalog.impl.OpenSPGCatalog; import com.antgroup.openspg.reasoner.common.graph.vertex.IVertexId; @@ -106,7 +106,7 @@ private List reasoning(BaseAdvancedRecord record, ConceptList con } // then run causal reasoning logic - for (LogicalCausationSemantic leadTo : conceptList.getLogicalCausation()) { + for (TripleSemantic leadTo : conceptList.getLogicalCausation()) { spgRecords = causalConceptReasoner.reason(spgRecords, leadTo); } return spgRecords; diff --git a/builder/core/src/main/java/com/antgroup/openspg/builder/core/reason/impl/CausalConceptReasoner.java b/builder/core/src/main/java/com/antgroup/openspg/builder/core/reason/impl/CausalConceptReasoner.java index 885d643d7..b8cd6bf19 100644 --- a/builder/core/src/main/java/com/antgroup/openspg/builder/core/reason/impl/CausalConceptReasoner.java +++ b/builder/core/src/main/java/com/antgroup/openspg/builder/core/reason/impl/CausalConceptReasoner.java @@ -21,7 +21,7 @@ import com.antgroup.openspg.builder.model.record.property.SPGPropertyRecord; import com.antgroup.openspg.builder.model.record.property.SPGPropertyValue; import com.antgroup.openspg.core.schema.model.semantic.DynamicTaxonomySemantic; -import com.antgroup.openspg.core.schema.model.semantic.LogicalCausationSemantic; +import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.core.schema.model.semantic.SystemPredicateEnum; import com.antgroup.openspg.core.schema.model.type.ConceptList; import com.antgroup.openspg.reasoner.common.graph.vertex.IVertexId; @@ -37,7 +37,7 @@ import org.apache.commons.collections4.CollectionUtils; import scala.Tuple2; -public class CausalConceptReasoner implements ConceptReasoner { +public class CausalConceptReasoner implements ConceptReasoner { @Setter private InductiveConceptReasoner inductiveConceptReasoner; @Setter private BuilderCatalog builderCatalog; @@ -46,7 +46,7 @@ public class CausalConceptReasoner implements ConceptReasoner reason( - List records, LogicalCausationSemantic conceptSemantic) { + List records, TripleSemantic conceptSemantic) { List results = new ArrayList<>(records); propagate(records, conceptSemantic, results); return results; @@ -54,7 +54,7 @@ public List reason( private void propagate( List spgRecords, - LogicalCausationSemantic conceptSemantic, + TripleSemantic conceptSemantic, List results) { List toPropagated = new ArrayList<>(); for (BaseSPGRecord spgRecord : spgRecords) { @@ -102,7 +102,7 @@ private void propagate( nextSpgRecords = inductiveConceptReasoner.reason(nextSpgRecords, belongTo); } - for (LogicalCausationSemantic nextLeadTo : + for (TripleSemantic nextLeadTo : conceptList.getLogicalCausation(conceptSemantic.getObjectIdentifier())) { propagate(nextSpgRecords, nextLeadTo, results); } @@ -110,7 +110,7 @@ private void propagate( } } - private List leadTo(BaseAdvancedRecord record, LogicalCausationSemantic leadTo) { + private List leadTo(BaseAdvancedRecord record, TripleSemantic leadTo) { LocalReasonerTask reasonerTask = new LocalReasonerTask(); reasonerTask.setCatalog(catalog); diff --git a/python/knext/knext/examples/supplychain/schema/supplychain_schema_helper.py b/python/knext/knext/examples/supplychain/schema/supplychain_schema_helper.py index c503a91d7..60a6a3a7a 100644 --- a/python/knext/knext/examples/supplychain/schema/supplychain_schema_helper.py +++ b/python/knext/knext/examples/supplychain/schema/supplychain_schema_helper.py @@ -15,133 +15,159 @@ # PLEASE DO NOT MODIFY THIS FILE!!! # -from knext.schema.model.schema_helper import ( - SPGTypeHelper, - PropertyHelper, - RelationHelper, -) +from knext.schema.model.schema_helper import SPGTypeHelper, PropertyHelper, RelationHelper class SupplyChain: + class Company(SPGTypeHelper): + class fundTrans(RelationHelper): - transAmt = PropertyHelper("transAmt") transDate = PropertyHelper("transDate") - - cashflowDiff6Month = PropertyHelper("cashflowDiff6Month") - name = PropertyHelper("name") - fundTrans3MonthIn = PropertyHelper("fundTrans3MonthIn") - cashflowDiff3Month = PropertyHelper("cashflowDiff3Month") + transAmt = PropertyHelper("transAmt") + cashflowDiff1Month = PropertyHelper("cashflowDiff1Month") - description = PropertyHelper("description") + fundTrans1MonthIn = PropertyHelper("fundTrans1MonthIn") + totalTransInAmt = PropertyHelper("totalTransInAmt") fundTrans1Month = PropertyHelper("fundTrans1Month") id = PropertyHelper("id") - fundTrans6MonthIn = PropertyHelper("fundTrans6MonthIn") + description = PropertyHelper("description") + name = PropertyHelper("name") fundTrans6Month = PropertyHelper("fundTrans6Month") - totalTransInAmt = PropertyHelper("totalTransInAmt") - fundTrans1MonthIn = PropertyHelper("fundTrans1MonthIn") + cashflowDiff6Month = PropertyHelper("cashflowDiff6Month") product = PropertyHelper("product") fundTrans3Month = PropertyHelper("fundTrans3Month") - + cashflowDiff3Month = PropertyHelper("cashflowDiff3Month") + fundTrans3MonthIn = PropertyHelper("fundTrans3MonthIn") + fundTrans6MonthIn = PropertyHelper("fundTrans6MonthIn") + mainSupply = RelationHelper("mainSupply") belongToIndustry = RelationHelper("belongToIndustry") sameLegalRepresentative = RelationHelper("sameLegalRepresentative") - + fundTrans = fundTrans("fundTrans") - + class CompanyEvent(SPGTypeHelper): - + + leadTo = PropertyHelper("leadTo") - name = PropertyHelper("name") - eventTime = PropertyHelper("eventTime") - description = PropertyHelper("description") + subject = PropertyHelper("subject") id = PropertyHelper("id") - trend = PropertyHelper("trend") + description = PropertyHelper("description") + name = PropertyHelper("name") belongTo = PropertyHelper("belongTo") + trend = PropertyHelper("trend") index = PropertyHelper("index") - subject = PropertyHelper("subject") - + eventTime = PropertyHelper("eventTime") + + + class Index(SPGTypeHelper): - - name = PropertyHelper("name") - stdId = PropertyHelper("stdId") + + alias = PropertyHelper("alias") - description = PropertyHelper("description") id = PropertyHelper("id") - - class Industry(SPGTypeHelper): - + description = PropertyHelper("description") name = PropertyHelper("name") stdId = PropertyHelper("stdId") + + + + class Industry(SPGTypeHelper): + + alias = PropertyHelper("alias") - description = PropertyHelper("description") id = PropertyHelper("id") - + description = PropertyHelper("description") + name = PropertyHelper("name") + stdId = PropertyHelper("stdId") + + + class Person(SPGTypeHelper): - + + certNo = PropertyHelper("certNo") + age = PropertyHelper("age") legalRepresentative = PropertyHelper("legalRepresentative") - name = PropertyHelper("name") - description = PropertyHelper("description") id = PropertyHelper("id") - age = PropertyHelper("age") - - class Product(SPGTypeHelper): - - name = PropertyHelper("name") - hasSupplyChain = PropertyHelper("hasSupplyChain") description = PropertyHelper("description") + name = PropertyHelper("name") + + + + class Product(SPGTypeHelper): + + id = PropertyHelper("id") - belongToIndustry = PropertyHelper("belongToIndustry") + description = PropertyHelper("description") + name = PropertyHelper("name") belongTo = PropertyHelper("belongTo") - + belongToIndustry = PropertyHelper("belongToIndustry") + hasSupplyChain = PropertyHelper("hasSupplyChain") + + + class ProductChainEvent(SPGTypeHelper): - - name = PropertyHelper("name") - eventTime = PropertyHelper("eventTime") - description = PropertyHelper("description") + + + subject = PropertyHelper("subject") id = PropertyHelper("id") - trend = PropertyHelper("trend") + description = PropertyHelper("description") + name = PropertyHelper("name") belongTo = PropertyHelper("belongTo") + trend = PropertyHelper("trend") index = PropertyHelper("index") - subject = PropertyHelper("subject") - + eventTime = PropertyHelper("eventTime") + leadTo = RelationHelper("leadTo") - + + class TaxOfCompanyEvent(SPGTypeHelper): - - name = PropertyHelper("name") - stdId = PropertyHelper("stdId") + + alias = PropertyHelper("alias") - description = PropertyHelper("description") id = PropertyHelper("id") - - class TaxOfProdEvent(SPGTypeHelper): - + description = PropertyHelper("description") name = PropertyHelper("name") stdId = PropertyHelper("stdId") + + + + class TaxOfProdEvent(SPGTypeHelper): + + alias = PropertyHelper("alias") - description = PropertyHelper("description") id = PropertyHelper("id") - - leadTo = RelationHelper("leadTo") - - class TaxOfProduct(SPGTypeHelper): - + description = PropertyHelper("description") name = PropertyHelper("name") stdId = PropertyHelper("stdId") + + leadTo = RelationHelper("leadTo") + + + class TaxOfProduct(SPGTypeHelper): + + alias = PropertyHelper("alias") - description = PropertyHelper("description") id = PropertyHelper("id") - - class Trend(SPGTypeHelper): - + description = PropertyHelper("description") name = PropertyHelper("name") stdId = PropertyHelper("stdId") + + + + class Trend(SPGTypeHelper): + + alias = PropertyHelper("alias") - description = PropertyHelper("description") id = PropertyHelper("id") - + description = PropertyHelper("description") + name = PropertyHelper("name") + stdId = PropertyHelper("stdId") + + + Company = Company("SupplyChain.Company") CompanyEvent = CompanyEvent("SupplyChain.CompanyEvent") Index = Index("SupplyChain.Index") @@ -153,5 +179,5 @@ class Trend(SPGTypeHelper): TaxOfProdEvent = TaxOfProdEvent("SupplyChain.TaxOfProdEvent") TaxOfProduct = TaxOfProduct("SupplyChain.TaxOfProduct") Trend = Trend("SupplyChain.Trend") - - pass + + pass \ No newline at end of file diff --git a/python/knext/knext/schema/marklang/concept_rule_ml.py b/python/knext/knext/schema/marklang/concept_rule_ml.py index 017953c40..f8c6b0000 100644 --- a/python/knext/knext/schema/marklang/concept_rule_ml.py +++ b/python/knext/knext/schema/marklang/concept_rule_ml.py @@ -9,7 +9,7 @@ # 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. - +import os import re from knext.schema import rest @@ -28,6 +28,8 @@ class SPGConceptRuleMarkLang: rule_text = "" src_concept = () dst_concept = () + predicate = None + is_reasoning = False def __init__(self, filename): self.current_line_num = 0 @@ -52,6 +54,48 @@ def parse_concept(self, expression): self.namespace = namespace_match.group(1) return + reasoning_concept_match = re.match( + r"^\(`([a-zA-Z0-9\.]+)`/`([^`]+)`\):$", + expression, + ) + if reasoning_concept_match: + assert self.namespace is not None, self.error_msg( + "please define namespace first" + ) + + self.dst_concept = (reasoning_concept_match.group(1), reasoning_concept_match.group(2)) + self.is_reasoning = True + return + + reasoning_po_match = re.match( + r"^\[([^\]]+)\]->\(`([a-zA-Z0-9\.]+)`/`([^`]+)`\):$", + expression, + ) + if reasoning_po_match: + assert self.namespace is not None, self.error_msg( + "please define namespace first" + ) + + self.predicate = reasoning_po_match.group(1) + self.dst_concept = (reasoning_po_match.group(2), reasoning_po_match.group(3)) + self.is_reasoning = True + return + + reasoning_spo_match = re.match( + r"^\(`([a-zA-Z0-9\.]+)`/`([^`]+)`\)-\[([^\]]+)\]->\(`([a-zA-Z0-9\.]+)`/`([^`]+)`\):$", + expression, + ) + if reasoning_spo_match: + assert self.namespace is not None, self.error_msg( + "please define namespace first" + ) + + self.src_concept = (reasoning_spo_match.group(1), reasoning_spo_match.group(2)) + self.predicate = reasoning_spo_match.group(3) + self.dst_concept = (reasoning_spo_match.group(4), reasoning_spo_match.group(5)) + self.is_reasoning = True + return + type_match = re.match( r"^`([a-zA-Z0-9\.]+)`/`([^`]+)`:(\s*?([a-zA-Z0-9\.]+)/`([^`]+)`)?$", expression, @@ -99,7 +143,13 @@ def complete_rule(self, rule): if not match: subject_type = None subject_name = None - if self.dst_concept[0] is not None: + if self.is_reasoning: + predicate_name = self.predicate + subject_type = self.src_concept[0] if len(self.src_concept) > 0 else None + subject_name = self.src_concept[1] if len(self.src_concept) > 0 else None + object_type = self.dst_concept[0] if len(self.dst_concept) > 0 else None + object_name = self.dst_concept[1] if len(self.dst_concept) > 0 else None + elif self.dst_concept[0] is not None: predicate_name = "leadTo" subject_type = f"{self.namespace}.{self.src_concept[0]}" subject_name = self.src_concept[1] @@ -124,7 +174,24 @@ def complete_rule(self, rule): subject_type = spg_type.name break - if subject_name is None: + if self.is_reasoning: + if subject_type is None and self.predicate is None: + head = ( + f"Define (o:`{object_type}`/`{object_name}`)" + + " {\n" + ) + elif subject_type is None and self.predicate is not None: + head = ( + f"Define [p:{predicate_name}]->(o:`{object_type}`/`{object_name}`)" + + " {\n" + ) + else: + head = ( + f"Define (s:`{object_type}`/`{object_name}`)-[p:{predicate_name}]->" + f"(o:`{object_type}`/`{object_name}`)" + + " {\n" + ) + elif subject_name is None: head = ( f"Define (s:{subject_type})-[p:{predicate_name}]->(o:`{object_type}`/`{object_name}`)" + " {\n" @@ -191,13 +258,32 @@ def clear_session(self): self.src_concept = () self.dst_concept = () self.rule_text = "" + self.predicate = None + self.is_reasoning = False def submit_rule(self): """ submit the rule definition, make them available for inference """ - if self.dst_concept[0] is None: + if self.is_reasoning: + # reasoning rule + self.concept_client.concept_define_logical_causation_post( + define_logical_causation_request=rest.DefineLogicalCausationRequest( + subject_concept_type_name="Thing" if len( + self.src_concept) == 0 else f"{self.namespace}.{self.src_concept[0]}", + subject_concept_name="1" if len(self.src_concept) == 0 else self.src_concept[1], + predicate_name="_conclude" if self.predicate is None else self.predicate, + object_concept_type_name=f"{self.namespace}.{self.dst_concept[0]}", + object_concept_name=self.dst_concept[1], + dsl=self.rule_text, + ) + ) + print( + f"Defined reasoning rule for `{self.dst_concept[0]}`/`{self.dst_concept[1]}`" + ) + + elif self.dst_concept[0] is None: # belongTo rule self.concept_client.concept_define_dynamic_taxonomy_post( define_dynamic_taxonomy_request=rest.DefineDynamicTaxonomyRequest( @@ -279,3 +365,8 @@ def load_script(self, filename): # if rule is the last line of file, then submit it if len(self.rule_text) > 0: self.submit_rule() + + +if __name__ == '__main__': + os.environ["KNEXT_PROJECT_ID"] = "1" + s = SPGConceptRuleMarkLang("/Users/matthew/Downloads/concept.rule") diff --git a/python/knext/knext/schema/rest/models/concept/define_logical_causation_request.py b/python/knext/knext/schema/rest/models/concept/define_logical_causation_request.py index 90bb8b831..720fb186b 100644 --- a/python/knext/knext/schema/rest/models/concept/define_logical_causation_request.py +++ b/python/knext/knext/schema/rest/models/concept/define_logical_causation_request.py @@ -50,6 +50,7 @@ class DefineLogicalCausationRequest(object): "object_concept_type_name": "str", "object_concept_name": "str", "dsl": "str", + "semanticType": "str", } attribute_map = { @@ -59,6 +60,7 @@ class DefineLogicalCausationRequest(object): "object_concept_type_name": "objectConceptTypeName", "object_concept_name": "objectConceptName", "dsl": "dsl", + "semantic_type": "semanticType", } def __init__( @@ -69,6 +71,7 @@ def __init__( object_concept_type_name=None, object_concept_name=None, dsl=None, + semantic_type=None, local_vars_configuration=None, ): # noqa: E501 """DefineLogicalCausationRequest - a model defined in OpenAPI""" # noqa: E501 @@ -82,6 +85,7 @@ def __init__( self._object_concept_type_name = None self._object_concept_name = None self._dsl = None + self._semantic_type = None self.discriminator = None if subject_concept_type_name is not None: @@ -96,6 +100,8 @@ def __init__( self.object_concept_name = object_concept_name if dsl is not None: self.dsl = dsl + if semantic_type is not None: + self.semantic_type = semantic_type @property def subject_concept_type_name(self): @@ -223,6 +229,14 @@ def dsl(self, dsl): self._dsl = dsl + @property + def semantic_type(self): + return self._semantic_type + + @semantic_type.setter + def semantic_type(self, semantic_type): + self._semantic_type = semantic_type + def to_dict(self): """Returns the model properties as a dict""" result = {} diff --git a/python/knext/knext/schema/rest/models/concept/remove_logical_causation_request.py b/python/knext/knext/schema/rest/models/concept/remove_logical_causation_request.py index a65a3d78c..80bae1e3b 100644 --- a/python/knext/knext/schema/rest/models/concept/remove_logical_causation_request.py +++ b/python/knext/knext/schema/rest/models/concept/remove_logical_causation_request.py @@ -49,6 +49,7 @@ class RemoveLogicalCausationRequest(object): "predicate_name": "str", "object_concept_type_name": "str", "object_concept_name": "str", + "semanticType": "str", } attribute_map = { @@ -57,6 +58,7 @@ class RemoveLogicalCausationRequest(object): "predicate_name": "predicateName", "object_concept_type_name": "objectConceptTypeName", "object_concept_name": "objectConceptName", + "semantic_type": "semanticType", } def __init__( @@ -66,6 +68,7 @@ def __init__( predicate_name=None, object_concept_type_name=None, object_concept_name=None, + semantic_type=None, local_vars_configuration=None, ): # noqa: E501 """RemoveLogicalCausationRequest - a model defined in OpenAPI""" # noqa: E501 @@ -78,6 +81,7 @@ def __init__( self._predicate_name = None self._object_concept_type_name = None self._object_concept_name = None + self._semantic_type = None self.discriminator = None if subject_concept_type_name is not None: @@ -90,6 +94,8 @@ def __init__( self.object_concept_type_name = object_concept_type_name if object_concept_name is not None: self.object_concept_name = object_concept_name + if semantic_type is not None: + self.semantic_type = semantic_type @property def subject_concept_type_name(self): @@ -196,6 +202,14 @@ def object_concept_name(self, object_concept_name): self._object_concept_name = object_concept_name + @property + def semantic_type(self): + return self._semantic_type + + @semantic_type.setter + def semantic_type(self, semantic_type): + self._semantic_type = semantic_type + def to_dict(self): """Returns the model properties as a dict""" result = {} diff --git a/server/api/facade/src/main/java/com/antgroup/openspg/server/api/facade/SchemaJsonUtils.java b/server/api/facade/src/main/java/com/antgroup/openspg/server/api/facade/SchemaJsonUtils.java index dbbc50eae..f5cff1f82 100644 --- a/server/api/facade/src/main/java/com/antgroup/openspg/server/api/facade/SchemaJsonUtils.java +++ b/server/api/facade/src/main/java/com/antgroup/openspg/server/api/facade/SchemaJsonUtils.java @@ -30,7 +30,7 @@ import com.antgroup.openspg.core.schema.model.identifier.SPGTypeIdentifier; import com.antgroup.openspg.core.schema.model.semantic.BaseConceptSemantic; import com.antgroup.openspg.core.schema.model.semantic.DynamicTaxonomySemantic; -import com.antgroup.openspg.core.schema.model.semantic.LogicalCausationSemantic; +import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.core.schema.model.type.BaseSPGType; import com.antgroup.openspg.core.schema.model.type.BasicType.DoubleBasicType; import com.antgroup.openspg.core.schema.model.type.BasicType.LongBasicType; @@ -95,7 +95,7 @@ public class SchemaJsonUtils { .registerTypeAdapterFactory( RuntimeTypeAdapterFactory.of(BaseConceptSemantic.class, DEFAULT_TYPE_FIELD_NAME) .registerSubtype(DynamicTaxonomySemantic.class) - .registerSubtype(LogicalCausationSemantic.class) + .registerSubtype(TripleSemantic.class) .recognizeSubtypes()) // BaseReasonerReceipt .setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) diff --git a/server/api/facade/src/main/java/com/antgroup/openspg/server/api/facade/client/ConceptFacade.java b/server/api/facade/src/main/java/com/antgroup/openspg/server/api/facade/client/ConceptFacade.java index 027ae2933..6b9b27337 100644 --- a/server/api/facade/src/main/java/com/antgroup/openspg/server/api/facade/client/ConceptFacade.java +++ b/server/api/facade/src/main/java/com/antgroup/openspg/server/api/facade/client/ConceptFacade.java @@ -14,9 +14,9 @@ package com.antgroup.openspg.server.api.facade.client; import com.antgroup.openspg.core.schema.model.semantic.request.DefineDynamicTaxonomyRequest; -import com.antgroup.openspg.core.schema.model.semantic.request.DefineLogicalCausationRequest; +import com.antgroup.openspg.core.schema.model.semantic.request.DefineTripleSemanticRequest; import com.antgroup.openspg.core.schema.model.semantic.request.RemoveDynamicTaxonomyRequest; -import com.antgroup.openspg.core.schema.model.semantic.request.RemoveLogicalCausationRequest; +import com.antgroup.openspg.core.schema.model.semantic.request.RemoveTripleSemanticRequest; import com.antgroup.openspg.core.schema.model.type.ConceptList; import com.antgroup.openspg.server.api.facade.ApiResponse; import com.antgroup.openspg.server.api.facade.dto.schema.request.ConceptRequest; @@ -53,7 +53,7 @@ public interface ConceptFacade { * @param request The Request to save concept relation * @return true or false */ - ApiResponse defineLogicalCausation(DefineLogicalCausationRequest request); + ApiResponse defineLogicalCausation(DefineTripleSemanticRequest request); /** * Remove dynamic taxonomy rule of concept. @@ -69,5 +69,5 @@ public interface ConceptFacade { * @param request The request to delete concept relation * @return true or false */ - ApiResponse removeLogicalCausation(RemoveLogicalCausationRequest request); + ApiResponse removeLogicalCausation(RemoveTripleSemanticRequest request); } diff --git a/server/api/http-client/src/main/java/com/antgroup/openspg/server/api/http/client/HttpConceptFacade.java b/server/api/http-client/src/main/java/com/antgroup/openspg/server/api/http/client/HttpConceptFacade.java index e5b7aff4b..573ca8a01 100644 --- a/server/api/http-client/src/main/java/com/antgroup/openspg/server/api/http/client/HttpConceptFacade.java +++ b/server/api/http-client/src/main/java/com/antgroup/openspg/server/api/http/client/HttpConceptFacade.java @@ -14,9 +14,9 @@ package com.antgroup.openspg.server.api.http.client; import com.antgroup.openspg.core.schema.model.semantic.request.DefineDynamicTaxonomyRequest; -import com.antgroup.openspg.core.schema.model.semantic.request.DefineLogicalCausationRequest; +import com.antgroup.openspg.core.schema.model.semantic.request.DefineTripleSemanticRequest; import com.antgroup.openspg.core.schema.model.semantic.request.RemoveDynamicTaxonomyRequest; -import com.antgroup.openspg.core.schema.model.semantic.request.RemoveLogicalCausationRequest; +import com.antgroup.openspg.core.schema.model.semantic.request.RemoveTripleSemanticRequest; import com.antgroup.openspg.core.schema.model.type.ConceptList; import com.antgroup.openspg.server.api.facade.ApiResponse; import com.antgroup.openspg.server.api.facade.client.ConceptFacade; @@ -37,7 +37,7 @@ public ApiResponse defineDynamicTaxonomy(DefineDynamicTaxonomyRequest r } @Override - public ApiResponse defineLogicalCausation(DefineLogicalCausationRequest request) { + public ApiResponse defineLogicalCausation(DefineTripleSemanticRequest request) { return ForestUtils.call(ConceptForestClient.class, c -> c.defineLogicalCausation(request)); } @@ -47,7 +47,7 @@ public ApiResponse removeDynamicTaxonomy(RemoveDynamicTaxonomyRequest r } @Override - public ApiResponse removeLogicalCausation(RemoveLogicalCausationRequest request) { + public ApiResponse removeLogicalCausation(RemoveTripleSemanticRequest request) { return ForestUtils.call(ConceptForestClient.class, c -> c.removeLogicalCausation(request)); } } diff --git a/server/api/http-client/src/main/java/com/antgroup/openspg/server/api/http/client/forest/client/ConceptForestClient.java b/server/api/http-client/src/main/java/com/antgroup/openspg/server/api/http/client/forest/client/ConceptForestClient.java index 91bd6a757..25d069ca6 100644 --- a/server/api/http-client/src/main/java/com/antgroup/openspg/server/api/http/client/forest/client/ConceptForestClient.java +++ b/server/api/http-client/src/main/java/com/antgroup/openspg/server/api/http/client/forest/client/ConceptForestClient.java @@ -14,9 +14,9 @@ package com.antgroup.openspg.server.api.http.client.forest.client; import com.antgroup.openspg.core.schema.model.semantic.request.DefineDynamicTaxonomyRequest; -import com.antgroup.openspg.core.schema.model.semantic.request.DefineLogicalCausationRequest; +import com.antgroup.openspg.core.schema.model.semantic.request.DefineTripleSemanticRequest; import com.antgroup.openspg.core.schema.model.semantic.request.RemoveDynamicTaxonomyRequest; -import com.antgroup.openspg.core.schema.model.semantic.request.RemoveLogicalCausationRequest; +import com.antgroup.openspg.core.schema.model.semantic.request.RemoveTripleSemanticRequest; import com.antgroup.openspg.core.schema.model.type.ConceptList; import com.antgroup.openspg.server.api.facade.dto.schema.request.ConceptRequest; import com.antgroup.openspg.server.api.http.client.util.HttpClientConstants; @@ -42,11 +42,11 @@ public interface ConceptForestClient { ForestResponse defineDynamicTaxonomy(@JSONBody DefineDynamicTaxonomyRequest request); @Post(value = "/public/v1/concept/defineLogicalCausation") - ForestResponse defineLogicalCausation(@JSONBody DefineLogicalCausationRequest request); + ForestResponse defineLogicalCausation(@JSONBody DefineTripleSemanticRequest request); @Post(value = "/public/v1/concept/removeDynamicTaxonomy") ForestResponse removeDynamicTaxonomy(@JSONBody RemoveDynamicTaxonomyRequest request); @Post(value = "/public/v1/concept/removeLogicalCausation") - ForestResponse removeLogicalCausation(@JSONBody RemoveLogicalCausationRequest request); + ForestResponse removeLogicalCausation(@JSONBody RemoveTripleSemanticRequest request); } diff --git a/server/api/http-server/src/main/java/com/antgroup/openspg/server/api/http/server/openapi/ConceptController.java b/server/api/http-server/src/main/java/com/antgroup/openspg/server/api/http/server/openapi/ConceptController.java index 78229cbe2..e2cd9cc19 100644 --- a/server/api/http-server/src/main/java/com/antgroup/openspg/server/api/http/server/openapi/ConceptController.java +++ b/server/api/http-server/src/main/java/com/antgroup/openspg/server/api/http/server/openapi/ConceptController.java @@ -14,9 +14,9 @@ package com.antgroup.openspg.server.api.http.server.openapi; import com.antgroup.openspg.core.schema.model.semantic.request.DefineDynamicTaxonomyRequest; -import com.antgroup.openspg.core.schema.model.semantic.request.DefineLogicalCausationRequest; +import com.antgroup.openspg.core.schema.model.semantic.request.DefineTripleSemanticRequest; import com.antgroup.openspg.core.schema.model.semantic.request.RemoveDynamicTaxonomyRequest; -import com.antgroup.openspg.core.schema.model.semantic.request.RemoveLogicalCausationRequest; +import com.antgroup.openspg.core.schema.model.semantic.request.RemoveTripleSemanticRequest; import com.antgroup.openspg.core.schema.model.type.ConceptList; import com.antgroup.openspg.server.api.facade.dto.schema.request.ConceptRequest; import com.antgroup.openspg.server.api.http.server.BaseController; @@ -81,7 +81,7 @@ public Boolean action() { @RequestMapping(value = "/defineLogicalCausation", method = RequestMethod.POST) @ResponseBody public ResponseEntity defineLogicalCausation( - @RequestBody DefineLogicalCausationRequest request) { + @RequestBody DefineTripleSemanticRequest request) { return HttpBizTemplate.execute( new HttpBizCallback() { @Override @@ -129,7 +129,7 @@ public Boolean action() { @RequestMapping(value = "/removeLogicalCausation", method = RequestMethod.POST) @ResponseBody public ResponseEntity removeLogicalCausation( - @RequestBody RemoveLogicalCausationRequest request) { + @RequestBody RemoveTripleSemanticRequest request) { return HttpBizTemplate.execute( new HttpBizCallback() { @Override diff --git a/server/biz/schema/src/main/java/com/antgroup/openspg/server/biz/schema/ConceptManager.java b/server/biz/schema/src/main/java/com/antgroup/openspg/server/biz/schema/ConceptManager.java index 0e0fd2453..1f78030f2 100644 --- a/server/biz/schema/src/main/java/com/antgroup/openspg/server/biz/schema/ConceptManager.java +++ b/server/biz/schema/src/main/java/com/antgroup/openspg/server/biz/schema/ConceptManager.java @@ -14,9 +14,9 @@ package com.antgroup.openspg.server.biz.schema; import com.antgroup.openspg.core.schema.model.semantic.request.DefineDynamicTaxonomyRequest; -import com.antgroup.openspg.core.schema.model.semantic.request.DefineLogicalCausationRequest; +import com.antgroup.openspg.core.schema.model.semantic.request.DefineTripleSemanticRequest; import com.antgroup.openspg.core.schema.model.semantic.request.RemoveDynamicTaxonomyRequest; -import com.antgroup.openspg.core.schema.model.semantic.request.RemoveLogicalCausationRequest; +import com.antgroup.openspg.core.schema.model.semantic.request.RemoveTripleSemanticRequest; import com.antgroup.openspg.core.schema.model.type.ConceptList; /** Provide method to manage concept */ @@ -41,14 +41,14 @@ public interface ConceptManager { * * @param request request */ - void defineLogicalCausation(DefineLogicalCausationRequest request); + void defineLogicalCausation(DefineTripleSemanticRequest request); /** * remove logical causation. * * @param request */ - void removeLogicalCausation(RemoveLogicalCausationRequest request); + void removeLogicalCausation(RemoveTripleSemanticRequest request); /** * Get concept detail by concept type. diff --git a/server/biz/schema/src/main/java/com/antgroup/openspg/server/biz/schema/impl/ConceptManagerImpl.java b/server/biz/schema/src/main/java/com/antgroup/openspg/server/biz/schema/impl/ConceptManagerImpl.java index d5db799d0..9a126a8c5 100644 --- a/server/biz/schema/src/main/java/com/antgroup/openspg/server/biz/schema/impl/ConceptManagerImpl.java +++ b/server/biz/schema/src/main/java/com/antgroup/openspg/server/biz/schema/impl/ConceptManagerImpl.java @@ -20,20 +20,21 @@ import com.antgroup.openspg.core.schema.model.identifier.SPGTypeIdentifier; import com.antgroup.openspg.core.schema.model.semantic.BaseConceptSemantic; import com.antgroup.openspg.core.schema.model.semantic.DynamicTaxonomySemantic; -import com.antgroup.openspg.core.schema.model.semantic.LogicalCausationSemantic; +import com.antgroup.openspg.core.schema.model.semantic.SPGOntologyEnum; +import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.core.schema.model.semantic.LogicalRule; import com.antgroup.openspg.core.schema.model.semantic.RuleStatusEnum; import com.antgroup.openspg.core.schema.model.semantic.request.DefineDynamicTaxonomyRequest; -import com.antgroup.openspg.core.schema.model.semantic.request.DefineLogicalCausationRequest; +import com.antgroup.openspg.core.schema.model.semantic.request.DefineTripleSemanticRequest; import com.antgroup.openspg.core.schema.model.semantic.request.RemoveDynamicTaxonomyRequest; -import com.antgroup.openspg.core.schema.model.semantic.request.RemoveLogicalCausationRequest; +import com.antgroup.openspg.core.schema.model.semantic.request.RemoveTripleSemanticRequest; import com.antgroup.openspg.core.schema.model.type.Concept; import com.antgroup.openspg.core.schema.model.type.ConceptList; import com.antgroup.openspg.server.biz.common.util.AssertUtils; import com.antgroup.openspg.server.biz.schema.ConceptManager; import com.antgroup.openspg.server.common.model.UserInfo; import com.antgroup.openspg.server.core.schema.service.concept.ConceptSemanticService; -import com.antgroup.openspg.server.core.schema.service.semantic.model.LogicalCausationQuery; +import com.antgroup.openspg.server.core.schema.service.semantic.model.TripleSemanticQuery; import com.google.common.collect.Lists; import java.util.ArrayList; import java.util.HashMap; @@ -92,7 +93,7 @@ public void removeDynamicTaxonomy(RemoveDynamicTaxonomyRequest request) { @Override public ConceptList getConceptDetail(String conceptTypeName, String conceptName) { // get logical causation semantic between concepts - List conceptRelationSemantics = + List conceptRelationSemantics = this.getLogicalCausation(conceptTypeName, conceptName); // get dynamic taxonomy semantic of concept @@ -121,7 +122,7 @@ public ConceptList getConceptDetail(String conceptTypeName, String conceptName) } @Override - public void defineLogicalCausation(DefineLogicalCausationRequest request) { + public void defineLogicalCausation(DefineTripleSemanticRequest request) { AssertUtils.assertParamObjectIsNotNull("dsl", request.getDsl()); LogicalRule logicalRule = new LogicalRule( @@ -132,19 +133,20 @@ public void defineLogicalCausation(DefineLogicalCausationRequest request) { RuleStatusEnum.PROD, request.getDsl(), new UserInfo(SchemaConstants.DEFAULT_USER_ID, null)); - LogicalCausationSemantic conceptSemantic = - new LogicalCausationSemantic( + TripleSemantic conceptSemantic = + new TripleSemantic( SPGTypeIdentifier.parse(request.getSubjectConceptTypeName()), new ConceptIdentifier(request.getSubjectConceptName()), new PredicateIdentifier(request.getPredicateName()), SPGTypeIdentifier.parse(request.getObjectConceptTypeName()), new ConceptIdentifier(request.getObjectConceptName()), - logicalRule); - conceptService.upsertLogicalCausationSemantic(conceptSemantic); + logicalRule, + StringUtils.isNotBlank(request.getSemanticType()) ? SPGOntologyEnum.valueOf(request.getSemanticType()) : null); + conceptService.upsertTripleSemantic(conceptSemantic); } @Override - public void removeLogicalCausation(RemoveLogicalCausationRequest request) { + public void removeLogicalCausation(RemoveTripleSemanticRequest request) { SPGTypeIdentifier subjectType = request.getSubjectConceptTypeName() == null ? null @@ -163,18 +165,19 @@ public void removeLogicalCausation(RemoveLogicalCausationRequest request) { ? null : new ConceptIdentifier(request.getObjectConceptName()); - LogicalCausationSemantic conceptSemantic = - new LogicalCausationSemantic( - subjectType, subjectName, predicateIdentifier, objectType, objectName, null); - conceptService.deleteLogicalCausationSemantic(conceptSemantic); + TripleSemantic conceptSemantic = + new TripleSemantic( + subjectType, subjectName, predicateIdentifier, objectType, objectName, null, + StringUtils.isNotBlank(request.getSemanticType()) ? SPGOntologyEnum.valueOf(request.getSemanticType()) : null); + conceptService.deleteTripleSemantic(conceptSemantic); } - private List getLogicalCausation( + private List getLogicalCausation( String subjectConceptTypeName, String subjectConceptName) { - LogicalCausationQuery query = new LogicalCausationQuery(); + TripleSemanticQuery query = new TripleSemanticQuery(); query.setSubjectTypeNames(Lists.newArrayList(subjectConceptTypeName)); query.setSubjectName(subjectConceptName); - return conceptService.queryLogicalCausationSemantic(query); + return conceptService.queryTripleSemantic(query); } private List getDynamicTaxonomy( diff --git a/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/SPGOntologyEnum.java b/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/SPGOntologyEnum.java index e66ffd781..f0bcbfc5c 100644 --- a/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/SPGOntologyEnum.java +++ b/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/SPGOntologyEnum.java @@ -29,7 +29,12 @@ public enum SPGOntologyEnum { SUB_PROPERTY, /** Concept instance. */ - CONCEPT; + CONCEPT, + + /** + * Reasoning concept instance. + */ + REASONING_CONCEPT; public static SPGOntologyEnum toEnum(String val) { for (SPGOntologyEnum resourceTypeEnum : SPGOntologyEnum.values()) { diff --git a/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/LogicalCausationSemantic.java b/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/TripleSemantic.java similarity index 73% rename from server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/LogicalCausationSemantic.java rename to server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/TripleSemantic.java index aa9d847bc..f48482fdc 100644 --- a/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/LogicalCausationSemantic.java +++ b/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/TripleSemantic.java @@ -18,7 +18,7 @@ import com.antgroup.openspg.core.schema.model.identifier.SPGTypeIdentifier; /** Inductive predicate defined between concepts, usually the name of the predicate is "leadTo". */ -public class LogicalCausationSemantic extends BaseConceptSemantic { +public class TripleSemantic extends BaseConceptSemantic { private static final long serialVersionUID = -1943418046354258381L; @@ -40,7 +40,10 @@ public class LogicalCausationSemantic extends BaseConceptSemantic { /** The details of the logic rule */ private final LogicalRule logicalRule; - public LogicalCausationSemantic( + /** The semantic type of the triple. */ + private final SPGOntologyEnum semanticType; + + public TripleSemantic( SPGTypeIdentifier subjectTypeIdentifier, ConceptIdentifier subjectIdentifier, PredicateIdentifier predicateIdentifier, @@ -53,6 +56,24 @@ public LogicalCausationSemantic( this.objectTypeIdentifier = objectTypeIdentifier; this.objectIdentifier = objectIdentifier; this.logicalRule = logicalRule; + this.semanticType = null; + } + + public TripleSemantic( + SPGTypeIdentifier subjectTypeIdentifier, + ConceptIdentifier subjectIdentifier, + PredicateIdentifier predicateIdentifier, + SPGTypeIdentifier objectTypeIdentifier, + ConceptIdentifier objectIdentifier, + LogicalRule logicalRule, + SPGOntologyEnum semanticType) { + this.subjectTypeIdentifier = subjectTypeIdentifier; + this.subjectIdentifier = subjectIdentifier; + this.predicateIdentifier = predicateIdentifier; + this.objectTypeIdentifier = objectTypeIdentifier; + this.objectIdentifier = objectIdentifier; + this.logicalRule = logicalRule; + this.semanticType = semanticType; } public SPGTypeIdentifier getSubjectTypeIdentifier() { @@ -78,4 +99,8 @@ public SPGTypeIdentifier getObjectTypeIdentifier() { public ConceptIdentifier getObjectIdentifier() { return objectIdentifier; } + + public SPGOntologyEnum getSemanticType() { + return semanticType; + } } diff --git a/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/request/DefineLogicalCausationRequest.java b/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/request/DefineTripleSemanticRequest.java similarity index 88% rename from server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/request/DefineLogicalCausationRequest.java rename to server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/request/DefineTripleSemanticRequest.java index e96166d74..78017aad8 100644 --- a/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/request/DefineLogicalCausationRequest.java +++ b/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/request/DefineTripleSemanticRequest.java @@ -16,7 +16,7 @@ import com.antgroup.openspg.server.common.model.base.BaseRequest; /** Request to define logical causation between concepts. */ -public class DefineLogicalCausationRequest extends BaseRequest { +public class DefineTripleSemanticRequest extends BaseRequest { private static final long serialVersionUID = 3663132552543144765L; @@ -38,6 +38,11 @@ public class DefineLogicalCausationRequest extends BaseRequest { /** The dsl content of logic rule defined in spo. */ private String dsl; + /** + * The semantic type of the triple. + */ + private String semanticType; + public String getSubjectConceptName() { return subjectConceptName; } @@ -85,4 +90,12 @@ public String getObjectConceptTypeName() { public void setObjectConceptTypeName(String objectConceptTypeName) { this.objectConceptTypeName = objectConceptTypeName; } + + public String getSemanticType() { + return semanticType; + } + + public void setSemanticType(String semanticType) { + this.semanticType = semanticType; + } } diff --git a/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/request/RemoveLogicalCausationRequest.java b/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/request/RemoveTripleSemanticRequest.java similarity index 84% rename from server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/request/RemoveLogicalCausationRequest.java rename to server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/request/RemoveTripleSemanticRequest.java index a88fdb994..2204c5ca9 100644 --- a/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/request/RemoveLogicalCausationRequest.java +++ b/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/request/RemoveTripleSemanticRequest.java @@ -16,9 +16,9 @@ import com.antgroup.openspg.server.common.model.base.BaseRequest; /** Request to remove logical causation between concepts. */ -public class RemoveLogicalCausationRequest extends BaseRequest { +public class RemoveTripleSemanticRequest extends BaseRequest { - private static final long serialVersionUID = 3663132552543144765L; + private static final long serialVersionUID = -3165344348700966342L; /** The unique name of subject concept type. */ private String subjectConceptTypeName; @@ -35,6 +35,11 @@ public class RemoveLogicalCausationRequest extends BaseRequest { /** The concept name of object in spo triple */ private String objectConceptName; + /** + * The semantic type of the triple. + */ + private String semanticType; + public String getSubjectConceptName() { return subjectConceptName; } @@ -74,4 +79,12 @@ public String getObjectConceptTypeName() { public void setObjectConceptTypeName(String objectConceptTypeName) { this.objectConceptTypeName = objectConceptTypeName; } + + public String getSemanticType() { + return semanticType; + } + + public void setSemanticType(String semanticType) { + this.semanticType = semanticType; + } } diff --git a/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/type/Concept.java b/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/type/Concept.java index 4654729ef..44ed415d1 100644 --- a/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/type/Concept.java +++ b/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/type/Concept.java @@ -16,7 +16,7 @@ import com.antgroup.openspg.core.schema.model.identifier.ConceptIdentifier; import com.antgroup.openspg.core.schema.model.semantic.BaseConceptSemantic; import com.antgroup.openspg.core.schema.model.semantic.DynamicTaxonomySemantic; -import com.antgroup.openspg.core.schema.model.semantic.LogicalCausationSemantic; +import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.core.schema.model.semantic.SystemPredicateEnum; import com.antgroup.openspg.server.common.model.base.BaseValObj; import java.util.List; @@ -63,14 +63,14 @@ public List getDynamicTaxonomySemantics() { .collect(Collectors.toList()); } - public List getLogicalCausationSemantics() { + public List getLogicalCausationSemantics() { return semantics.stream() .map( semantic -> { - if (!(semantic instanceof LogicalCausationSemantic)) { + if (!(semantic instanceof TripleSemantic)) { return null; } - LogicalCausationSemantic relationSemantic = (LogicalCausationSemantic) semantic; + TripleSemantic relationSemantic = (TripleSemantic) semantic; if (!SystemPredicateEnum.LEAD_TO .getName() .equals(relationSemantic.getPredicateIdentifier().getName())) { diff --git a/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/type/ConceptList.java b/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/type/ConceptList.java index 4c11589e1..fe82ad600 100644 --- a/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/type/ConceptList.java +++ b/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/type/ConceptList.java @@ -15,7 +15,7 @@ import com.antgroup.openspg.core.schema.model.identifier.ConceptIdentifier; import com.antgroup.openspg.core.schema.model.semantic.DynamicTaxonomySemantic; -import com.antgroup.openspg.core.schema.model.semantic.LogicalCausationSemantic; +import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.server.common.model.base.BaseValObj; import java.util.ArrayList; import java.util.List; @@ -50,16 +50,16 @@ public List getDynamicTaxonomyList(ConceptIdentifier co return semantics; } - public List getLogicalCausation() { - List semantics = new ArrayList<>(); + public List getLogicalCausation() { + List semantics = new ArrayList<>(); for (Concept concept : concepts) { semantics.addAll(concept.getLogicalCausationSemantics()); } return semantics; } - public List getLogicalCausation(ConceptIdentifier conceptName) { - List semantics = new ArrayList<>(); + public List getLogicalCausation(ConceptIdentifier conceptName) { + List semantics = new ArrayList<>(); for (Concept concept : concepts) { if (concept.getName().equals(conceptName)) { semantics.addAll(concept.getLogicalCausationSemantics()); diff --git a/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/concept/ConceptSemanticService.java b/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/concept/ConceptSemanticService.java index eb2841fc1..b846dc11b 100644 --- a/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/concept/ConceptSemanticService.java +++ b/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/concept/ConceptSemanticService.java @@ -16,8 +16,9 @@ import com.antgroup.openspg.core.schema.model.identifier.ConceptIdentifier; import com.antgroup.openspg.core.schema.model.identifier.SPGTypeIdentifier; import com.antgroup.openspg.core.schema.model.semantic.DynamicTaxonomySemantic; -import com.antgroup.openspg.core.schema.model.semantic.LogicalCausationSemantic; -import com.antgroup.openspg.server.core.schema.service.semantic.model.LogicalCausationQuery; +import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; +import com.antgroup.openspg.server.core.schema.service.semantic.model.TripleSemanticQuery; + import java.util.List; /** @@ -55,26 +56,26 @@ int deleteDynamicTaxonomySemantic( int upsertDynamicTaxonomySemantic(DynamicTaxonomySemantic dynamicTaxonomySemantic); /** - * Query logical causation between concepts by condition. + * Query triple between concepts by condition. * * @param query query condition * @return list of concept semantic */ - List queryLogicalCausationSemantic(LogicalCausationQuery query); + List queryTripleSemantic(TripleSemanticQuery query); /** - * delete logical causation semantic. + * delete triple semantic. * - * @param logicalCausationSemantic concept semantic model + * @param tripleSemantic concept semantic model * @return record count */ - int deleteLogicalCausationSemantic(LogicalCausationSemantic logicalCausationSemantic); + int deleteTripleSemantic(TripleSemantic tripleSemantic); /** - * Add logical causation semantic. + * Add triple semantic. * - * @param logicalCausationSemantic concept semantic model + * @param tripleSemantic concept semantic model * @return record count is created */ - int upsertLogicalCausationSemantic(LogicalCausationSemantic logicalCausationSemantic); + int upsertTripleSemantic(TripleSemantic tripleSemantic); } diff --git a/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/concept/convertor/ConceptSemanticConvertor.java b/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/concept/convertor/ConceptSemanticConvertor.java index 1d0d30560..d03571768 100644 --- a/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/concept/convertor/ConceptSemanticConvertor.java +++ b/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/concept/convertor/ConceptSemanticConvertor.java @@ -15,7 +15,7 @@ import com.antgroup.openspg.core.schema.model.identifier.ConceptIdentifier; import com.antgroup.openspg.core.schema.model.semantic.DynamicTaxonomySemantic; -import com.antgroup.openspg.core.schema.model.semantic.LogicalCausationSemantic; +import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.core.schema.model.semantic.LogicalRule; import com.antgroup.openspg.core.schema.model.semantic.RuleCode; import com.antgroup.openspg.core.schema.model.semantic.SPGOntologyEnum; @@ -67,9 +67,9 @@ public static SimpleSemantic convert(DynamicTaxonomySemantic semantic, Long taxo semantic.getLogicalRule() == null ? null : semantic.getLogicalRule().getCode()); } - public static SimpleSemantic convert(LogicalCausationSemantic semantic) { + public static SimpleSemantic convert(TripleSemantic semantic) { return new SimpleSemantic( - SPGOntologyEnum.CONCEPT, + semantic.getOntologyType() == null ? SPGOntologyEnum.CONCEPT : semantic.getOntologyType(), semantic.getSubjectIdentifier().getId(), semantic.getObjectIdentifier().getId(), semantic.getPredicateIdentifier(), @@ -78,13 +78,13 @@ public static SimpleSemantic convert(LogicalCausationSemantic semantic) { semantic.getLogicalRule() == null ? null : semantic.getLogicalRule().getCode()); } - public static List convertSemanticList( + public static List convertSemanticList( List simpleSemanticList, List logicalRuleList) { if (CollectionUtils.isEmpty(simpleSemanticList)) { return Collections.emptyList(); } - List semantics = new ArrayList<>(); + List semantics = new ArrayList<>(); Map logicalRuleMap = logicalRuleList.stream() .collect(Collectors.toMap(LogicalRule::getCode, Function.identity(), (o1, o2) -> o1)); @@ -95,9 +95,9 @@ public static List convertSemanticList( return semantics; } - public static LogicalCausationSemantic convertSemantic( + public static TripleSemantic convertSemantic( SimpleSemantic simpleSemantic, LogicalRule logicalRule) { - return new LogicalCausationSemantic( + return new TripleSemantic( simpleSemantic.getSubjectTypeIdentifier(), new ConceptIdentifier(simpleSemantic.getSubjectId()), simpleSemantic.getPredicateIdentifier(), diff --git a/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/concept/impl/ConceptSemanticServiceImpl.java b/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/concept/impl/ConceptSemanticServiceImpl.java index f60d500b4..fa735b3ed 100644 --- a/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/concept/impl/ConceptSemanticServiceImpl.java +++ b/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/concept/impl/ConceptSemanticServiceImpl.java @@ -16,7 +16,7 @@ import com.antgroup.openspg.core.schema.model.identifier.ConceptIdentifier; import com.antgroup.openspg.core.schema.model.identifier.SPGTypeIdentifier; import com.antgroup.openspg.core.schema.model.semantic.DynamicTaxonomySemantic; -import com.antgroup.openspg.core.schema.model.semantic.LogicalCausationSemantic; +import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.core.schema.model.semantic.LogicalRule; import com.antgroup.openspg.core.schema.model.semantic.RuleCode; import com.antgroup.openspg.core.schema.model.semantic.SPGOntologyEnum; @@ -25,7 +25,8 @@ import com.antgroup.openspg.server.core.schema.service.concept.convertor.ConceptSemanticConvertor; import com.antgroup.openspg.server.core.schema.service.predicate.repository.PropertyRepository; import com.antgroup.openspg.server.core.schema.service.semantic.LogicalRuleService; -import com.antgroup.openspg.server.core.schema.service.semantic.model.LogicalCausationQuery; +import com.antgroup.openspg.server.core.schema.service.semantic.model.TripleSemanticQuery; +import com.antgroup.openspg.server.core.schema.service.semantic.model.ReasoningConclusionQuery; import com.antgroup.openspg.server.core.schema.service.semantic.model.SimpleSemantic; import com.antgroup.openspg.server.core.schema.service.semantic.repository.SemanticRepository; import com.google.common.collect.Lists; @@ -57,8 +58,8 @@ public List queryDynamicTaxonomySemantic( return new ArrayList<>(); } - LogicalCausationQuery query = - new LogicalCausationQuery() + TripleSemanticQuery query = + new TripleSemanticQuery() .setPredicateName(SystemPredicateEnum.BELONG_TO.getName()) .setSubjectName(taxonomicRelationId.toString()) .setObjectTypeNames(Lists.newArrayList(conceptTypeIdentifier.toString())) @@ -137,7 +138,7 @@ public int upsertDynamicTaxonomySemantic(DynamicTaxonomySemantic dynamicTaxonomy } @Override - public List queryLogicalCausationSemantic(LogicalCausationQuery query) { + public List queryTripleSemantic(TripleSemanticQuery query) { List exist = semanticRepository.queryConceptSemanticByCond(query); if (CollectionUtils.isEmpty(exist)) { return Collections.emptyList(); @@ -153,7 +154,7 @@ public List queryLogicalCausationSemantic(LogicalCausa } @Override - public int deleteLogicalCausationSemantic(LogicalCausationSemantic conceptSemantic) { + public int deleteTripleSemantic(TripleSemantic conceptSemantic) { List exist = this.queryExistSemantic(conceptSemantic); if (CollectionUtils.isEmpty(exist)) { return 0; @@ -170,7 +171,7 @@ public int deleteLogicalCausationSemantic(LogicalCausationSemantic conceptSemant } @Override - public int upsertLogicalCausationSemantic(LogicalCausationSemantic conceptSemantic) { + public int upsertTripleSemantic(TripleSemantic conceptSemantic) { List exist = this.queryExistSemantic(conceptSemantic); if (CollectionUtils.isNotEmpty(exist)) { RuleCode ruleCode = exist.get(0).getRuleCode(); @@ -202,16 +203,17 @@ private void deleteLogicalRule(List ruleCodes) { logicalRuleService.deleteByRuleId(ruleCodes); } - private List queryExistSemantic(LogicalCausationSemantic conceptSemantic) { - LogicalCausationQuery query = - new LogicalCausationQuery() + private List queryExistSemantic(TripleSemantic conceptSemantic) { + TripleSemanticQuery query = + new TripleSemanticQuery() .setSubjectTypeNames( Lists.newArrayList(conceptSemantic.getSubjectTypeIdentifier().toString())) .setSubjectName(conceptSemantic.getSubjectIdentifier().getId()) .setPredicateName(conceptSemantic.getPredicateIdentifier().getName()) .setObjectTypeNames( Lists.newArrayList(conceptSemantic.getObjectTypeIdentifier().toString())) - .setObjectName(conceptSemantic.getObjectIdentifier().getId()); + .setObjectName(conceptSemantic.getObjectIdentifier().getId()) + .setSpgOntologyEnum(conceptSemantic.getSemanticType()); return semanticRepository.queryConceptSemanticByCond(query); } } diff --git a/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/semantic/model/ReasoningConclusionQuery.java b/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/semantic/model/ReasoningConclusionQuery.java new file mode 100644 index 000000000..dde8ba110 --- /dev/null +++ b/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/semantic/model/ReasoningConclusionQuery.java @@ -0,0 +1,35 @@ +/* + * 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.server.core.schema.service.semantic.model; + +import java.util.List; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +/** Query object of concept semantic. */ +@Getter +@Setter +@Accessors(chain = true) +public class ReasoningConclusionQuery { + + /** The list of object type name. */ + private List objectTypeNames; + + /** The object name. */ + private String objectName; + + /** The predicate name. */ + private String predicateName; +} diff --git a/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/semantic/model/LogicalCausationQuery.java b/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/semantic/model/TripleSemanticQuery.java similarity index 84% rename from server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/semantic/model/LogicalCausationQuery.java rename to server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/semantic/model/TripleSemanticQuery.java index 4326178be..52e8dcd0c 100644 --- a/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/semantic/model/LogicalCausationQuery.java +++ b/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/semantic/model/TripleSemanticQuery.java @@ -14,6 +14,8 @@ package com.antgroup.openspg.server.core.schema.service.semantic.model; import java.util.List; + +import com.antgroup.openspg.core.schema.model.semantic.SPGOntologyEnum; import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; @@ -22,7 +24,7 @@ @Getter @Setter @Accessors(chain = true) -public class LogicalCausationQuery { +public class TripleSemanticQuery { /** The list of subject type name */ private List subjectTypeNames; @@ -38,4 +40,9 @@ public class LogicalCausationQuery { /** The predicate name. */ private String predicateName; + + /** + * The type of spg ontology + */ + private SPGOntologyEnum spgOntologyEnum; } diff --git a/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/semantic/repository/SemanticRepository.java b/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/semantic/repository/SemanticRepository.java index 6d1707e1f..04baad5b3 100644 --- a/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/semantic/repository/SemanticRepository.java +++ b/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/semantic/repository/SemanticRepository.java @@ -13,9 +13,9 @@ package com.antgroup.openspg.server.core.schema.service.semantic.repository; -import com.antgroup.openspg.core.schema.model.semantic.LogicalCausationSemantic; +import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.core.schema.model.semantic.SPGOntologyEnum; -import com.antgroup.openspg.server.core.schema.service.semantic.model.LogicalCausationQuery; +import com.antgroup.openspg.server.core.schema.service.semantic.model.TripleSemanticQuery; import com.antgroup.openspg.server.core.schema.service.semantic.model.SimpleSemantic; import java.util.List; @@ -62,7 +62,7 @@ int deleteByObject( * @param conceptSemantic relation semantic of concept * @return record count */ - int deleteConceptSemantic(LogicalCausationSemantic conceptSemantic); + int deleteConceptSemantic(TripleSemantic conceptSemantic); /** * Query concept semantic record by condition. @@ -70,7 +70,7 @@ int deleteByObject( * @param query query condition * @return list of semantic record */ - List queryConceptSemanticByCond(LogicalCausationQuery query); + List queryConceptSemanticByCond(TripleSemanticQuery query); /** * Query semantic record by subject. diff --git a/server/infra/dao/src/main/java/com/antgroup/openspg/server/infra/dao/repository/schema/SemanticRepositoryImpl.java b/server/infra/dao/src/main/java/com/antgroup/openspg/server/infra/dao/repository/schema/SemanticRepositoryImpl.java index d2523ab55..889ff19e7 100644 --- a/server/infra/dao/src/main/java/com/antgroup/openspg/server/infra/dao/repository/schema/SemanticRepositoryImpl.java +++ b/server/infra/dao/src/main/java/com/antgroup/openspg/server/infra/dao/repository/schema/SemanticRepositoryImpl.java @@ -15,10 +15,10 @@ import com.antgroup.openspg.common.util.CollectionsUtils; import com.antgroup.openspg.common.util.StringUtils; -import com.antgroup.openspg.core.schema.model.semantic.LogicalCausationSemantic; +import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.core.schema.model.semantic.SPGOntologyEnum; import com.antgroup.openspg.server.common.service.SequenceRepository; -import com.antgroup.openspg.server.core.schema.service.semantic.model.LogicalCausationQuery; +import com.antgroup.openspg.server.core.schema.service.semantic.model.TripleSemanticQuery; import com.antgroup.openspg.server.core.schema.service.semantic.model.SimpleSemantic; import com.antgroup.openspg.server.core.schema.service.semantic.repository.SemanticRepository; import com.antgroup.openspg.server.infra.dao.dataobject.SemanticDO; @@ -101,7 +101,7 @@ public int deleteByObject( } @Override - public int deleteConceptSemantic(LogicalCausationSemantic conceptSemantic) { + public int deleteConceptSemantic(TripleSemantic conceptSemantic) { SemanticDOExample example = new SemanticDOExample(); SemanticDOExample.Criteria criteria = example.createCriteria(); if (conceptSemantic.getSubjectTypeIdentifier() != null) { @@ -123,10 +123,14 @@ public int deleteConceptSemantic(LogicalCausationSemantic conceptSemantic) { } @Override - public List queryConceptSemanticByCond(LogicalCausationQuery query) { + public List queryConceptSemanticByCond(TripleSemanticQuery query) { SemanticDOExample example = new SemanticDOExample(); SemanticDOExample.Criteria criteria = example.createCriteria(); - criteria.andResourceTypeEqualTo(SPGOntologyEnum.CONCEPT.name()); + if (query.getSpgOntologyEnum() == null) { + criteria.andResourceTypeEqualTo(SPGOntologyEnum.CONCEPT.name()); + } else { + criteria.andResourceTypeEqualTo(query.getSpgOntologyEnum().name()); + } if (CollectionUtils.isNotEmpty(query.getSubjectTypeNames())) { criteria.andSubjectMetaTypeIn(query.getSubjectTypeNames()); From 82589f1a147b84c62d34f986e2189461aa0aac56 Mon Sep 17 00:00:00 2001 From: matthewhyx Date: Thu, 9 May 2024 11:36:09 +0800 Subject: [PATCH 03/29] feat(schema): fix schema preload bug --- .../rest/models/concept/define_logical_causation_request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/knext/knext/schema/rest/models/concept/define_logical_causation_request.py b/python/knext/knext/schema/rest/models/concept/define_logical_causation_request.py index 720fb186b..5b17807d3 100644 --- a/python/knext/knext/schema/rest/models/concept/define_logical_causation_request.py +++ b/python/knext/knext/schema/rest/models/concept/define_logical_causation_request.py @@ -50,7 +50,7 @@ class DefineLogicalCausationRequest(object): "object_concept_type_name": "str", "object_concept_name": "str", "dsl": "str", - "semanticType": "str", + "semantic_type": "str", } attribute_map = { From 1bfa7ed4872c6bc4125145dc7ef6361c28987ed7 Mon Sep 17 00:00:00 2001 From: matthewhyx Date: Fri, 10 May 2024 21:11:42 +0800 Subject: [PATCH 04/29] feat(schema): support maintain reasoning rule --- .../knext/schema/marklang/concept_rule_ml.py | 134 +++++++++++++----- python/knext/knext/schema/rest/concept_api.py | 6 +- .../remove_logical_causation_request.py | 2 +- .../test/kgschema/SPGSchemaFacadeTest.groovy | 8 +- 4 files changed, 104 insertions(+), 46 deletions(-) diff --git a/python/knext/knext/schema/marklang/concept_rule_ml.py b/python/knext/knext/schema/marklang/concept_rule_ml.py index f8c6b0000..8c06cebe9 100644 --- a/python/knext/knext/schema/marklang/concept_rule_ml.py +++ b/python/knext/knext/schema/marklang/concept_rule_ml.py @@ -17,6 +17,16 @@ from knext.schema.model.base import SpgTypeEnum +def is_blank(text): + if not text: + return True + if len(text) == 0: + return True + if text.isspace(): + return True + return False + + class SPGConceptRuleMarkLang: """ SPG Concept Rule Mark Language Parser @@ -125,7 +135,7 @@ def parse_rule(self, rule): if len(strip_rule) > 2: if strip_rule.endswith("]]"): self.rule_quote_open = False - self.rule_text = strip_rule[2 : len(strip_rule) - 2].lstrip() + self.rule_text = strip_rule[2: len(strip_rule) - 2].lstrip() else: self.rule_text = strip_rule[2].lstrip() else: @@ -204,6 +214,8 @@ def complete_rule(self, rule): ) rule = head + rule rule += "\n}" + elif self.is_reasoning: + raise Exception(self.error_msg("Wrong format for reasoning rule")) # complete the namespace of concept type pattern = re.compile(r"\(([\w\s]*?:)`([\w\s\.]+)`/`([^`]+)`\)", re.IGNORECASE) @@ -268,50 +280,92 @@ def submit_rule(self): if self.is_reasoning: # reasoning rule - self.concept_client.concept_define_logical_causation_post( - define_logical_causation_request=rest.DefineLogicalCausationRequest( - subject_concept_type_name="Thing" if len( - self.src_concept) == 0 else f"{self.namespace}.{self.src_concept[0]}", - subject_concept_name="1" if len(self.src_concept) == 0 else self.src_concept[1], - predicate_name="_conclude" if self.predicate is None else self.predicate, - object_concept_type_name=f"{self.namespace}.{self.dst_concept[0]}", - object_concept_name=self.dst_concept[1], - dsl=self.rule_text, + if not is_blank(self.rule_text): + self.concept_client.concept_define_logical_causation_post( + define_logical_causation_request=rest.DefineLogicalCausationRequest( + subject_concept_type_name="Thing" if len( + self.src_concept) == 0 else f"{self.namespace}.{self.src_concept[0]}", + subject_concept_name="1" if len(self.src_concept) == 0 else self.src_concept[1], + predicate_name="_conclude" if self.predicate is None else self.predicate, + object_concept_type_name=f"{self.namespace}.{self.dst_concept[0]}", + object_concept_name=self.dst_concept[1], + dsl=self.rule_text, + ) + ) + print( + f"Defined reasoning rule for `{self.dst_concept[0]}`/`{self.dst_concept[1]}`" + ) + else: + self.concept_client.schema_remove_logical_causation_get( + remove_logical_causation_request=rest.RemoveLogicalCausationRequest( + subject_concept_type_name="Thing" if len( + self.src_concept) == 0 else f"{self.namespace}.{self.src_concept[0]}", + subject_concept_name="1" if len(self.src_concept) == 0 else self.src_concept[1], + predicate_name="_conclude" if self.predicate is None else self.predicate, + object_concept_type_name=f"{self.namespace}.{self.dst_concept[0]}", + object_concept_name=self.dst_concept[1], + ) + ) + print( + f"Removed reasoning rule for `{self.dst_concept[0]}`/`{self.dst_concept[1]}`" ) - ) - print( - f"Defined reasoning rule for `{self.dst_concept[0]}`/`{self.dst_concept[1]}`" - ) elif self.dst_concept[0] is None: # belongTo rule - self.concept_client.concept_define_dynamic_taxonomy_post( - define_dynamic_taxonomy_request=rest.DefineDynamicTaxonomyRequest( - concept_type_name=f"{self.namespace}.{self.src_concept[0]}", - concept_name=self.src_concept[1], - dsl=self.rule_text, + if not is_blank(self.rule_text): + self.concept_client.concept_define_dynamic_taxonomy_post( + define_dynamic_taxonomy_request=rest.DefineDynamicTaxonomyRequest( + concept_type_name=f"{self.namespace}.{self.src_concept[0]}", + concept_name=self.src_concept[1], + dsl=self.rule_text, + ) + ) + print( + f"Defined belongTo rule for `{self.src_concept[0]}`/`{self.src_concept[1]}`" + ) + else: + self.concept_client.concept_remove_dynamic_taxonomy_get( + remove_dynamic_taxonomy_request=rest.RemoveDynamicTaxonomyRequest( + object_concept_type_name=f"{self.namespace}.{self.src_concept[0]}", + object_concept_name=self.src_concept[1] + ) + ) + print( + f"Removed belongTo rule for `{self.src_concept[0]}`/`{self.src_concept[1]}`" ) - ) - print( - f"Defined belongTo rule for `{self.src_concept[0]}`/`{self.src_concept[1]}`" - ) else: # leadTo rule - self.concept_client.concept_define_logical_causation_post( - define_logical_causation_request=rest.DefineLogicalCausationRequest( - subject_concept_type_name=f"{self.namespace}.{self.src_concept[0]}", - subject_concept_name=self.src_concept[1], - predicate_name="leadTo", - object_concept_type_name=f"{self.namespace}.{self.dst_concept[0]}", - object_concept_name=self.dst_concept[1], - dsl=self.rule_text, + if not is_blank(self.rule_text): + self.concept_client.concept_define_logical_causation_post( + define_logical_causation_request=rest.DefineLogicalCausationRequest( + subject_concept_type_name=f"{self.namespace}.{self.src_concept[0]}", + subject_concept_name=self.src_concept[1], + predicate_name="leadTo", + object_concept_type_name=f"{self.namespace}.{self.dst_concept[0]}", + object_concept_name=self.dst_concept[1], + dsl=self.rule_text, + ) ) - ) - print( - f"Defined leadTo rule for " - f"`{self.src_concept[0]}`/`{self.src_concept[1]}` -> `{self.dst_concept[0]}`/`{self.dst_concept[1]}`" - ) + print( + f"Defined leadTo rule for " + f"`{self.src_concept[0]}`/`{self.src_concept[1]}` -> `{self.dst_concept[0]}`/`{self.dst_concept[1]}`" + ) + else: + self.concept_client.schema_remove_logical_causation_get( + remove_logical_causation_request=rest.RemoveLogicalCausationRequest( + subject_concept_type_name=f"{self.namespace}.{self.src_concept[0]}", + subject_concept_name=self.src_concept[1], + predicate_name="leadTo", + object_concept_type_name=f"{self.namespace}.{self.dst_concept[0]}", + object_concept_name=self.dst_concept[1] + ) + ) + print( + f"Removed leadTo rule for " + f"`{self.src_concept[0]}`/`{self.src_concept[1]}` -> `{self.dst_concept[0]}`/`{self.dst_concept[1]}`" + ) + self.clear_session() def load_script(self, filename): @@ -337,7 +391,8 @@ def load_script(self, filename): self.rule_quote_open = False if len(right_strip_line) > 2: self.rule_text += right_strip_line[: len(right_strip_line) - 2] - self.rule_text = self.complete_rule(self.rule_text) + if not is_blank(self.rule_text): + self.rule_text = self.complete_rule(self.rule_text) self.submit_rule() else: @@ -349,7 +404,10 @@ def load_script(self, filename): indent_count = len(line) - len(line.lstrip()) if indent_count == 0: # the line without indent is namespace definition or a concept definition - self.clear_session() + if len(self.src_concept) > 1 and is_blank(self.rule_text): + self.submit_rule() + else: + self.clear_session() self.parse_concept(strip_line) elif indent_count > last_indent_level: diff --git a/python/knext/knext/schema/rest/concept_api.py b/python/knext/knext/schema/rest/concept_api.py index 6f0c18740..e70893cc5 100644 --- a/python/knext/knext/schema/rest/concept_api.py +++ b/python/knext/knext/schema/rest/concept_api.py @@ -392,7 +392,7 @@ def concept_remove_dynamic_taxonomy_get_with_http_info( return self.api_client.call_api( "/concept/removeDynamicTaxonomy", - "GET", + "POST", path_params, query_params, header_params, @@ -513,8 +513,8 @@ def schema_remove_logical_causation_get_with_http_info( auth_settings = [] # noqa: E501 return self.api_client.call_api( - "/schema/removeLogicalCausation", - "GET", + "/concept/removeLogicalCausation", + "POST", path_params, query_params, header_params, diff --git a/python/knext/knext/schema/rest/models/concept/remove_logical_causation_request.py b/python/knext/knext/schema/rest/models/concept/remove_logical_causation_request.py index 80bae1e3b..6ddcd9806 100644 --- a/python/knext/knext/schema/rest/models/concept/remove_logical_causation_request.py +++ b/python/knext/knext/schema/rest/models/concept/remove_logical_causation_request.py @@ -49,7 +49,7 @@ class RemoveLogicalCausationRequest(object): "predicate_name": "str", "object_concept_type_name": "str", "object_concept_name": "str", - "semanticType": "str", + "semantic_type": "str", } attribute_map = { diff --git a/server/test/src/test/java/com/antgroup/openspg/test/kgschema/SPGSchemaFacadeTest.groovy b/server/test/src/test/java/com/antgroup/openspg/test/kgschema/SPGSchemaFacadeTest.groovy index d2660db44..8ce29db5f 100644 --- a/server/test/src/test/java/com/antgroup/openspg/test/kgschema/SPGSchemaFacadeTest.groovy +++ b/server/test/src/test/java/com/antgroup/openspg/test/kgschema/SPGSchemaFacadeTest.groovy @@ -26,9 +26,9 @@ import com.antgroup.openspg.core.schema.model.alter.SchemaDraft import com.antgroup.openspg.core.schema.model.predicate.Property import com.antgroup.openspg.core.schema.model.predicate.Relation import com.antgroup.openspg.core.schema.model.semantic.request.DefineDynamicTaxonomyRequest -import com.antgroup.openspg.core.schema.model.semantic.request.DefineLogicalCausationRequest +import com.antgroup.openspg.core.schema.model.semantic.request.DefineTripleSemanticRequest import com.antgroup.openspg.core.schema.model.semantic.request.RemoveDynamicTaxonomyRequest -import com.antgroup.openspg.core.schema.model.semantic.request.RemoveLogicalCausationRequest +import com.antgroup.openspg.core.schema.model.semantic.request.RemoveTripleSemanticRequest import com.antgroup.openspg.core.schema.model.type.BaseSPGType import com.antgroup.openspg.core.schema.model.type.ConceptList import com.antgroup.openspg.core.schema.model.type.ProjectSchema @@ -116,7 +116,7 @@ class SPGSchemaFacadeTest extends Specification { "{GraphStructure{} Rule{ R1: s.age >= 50}}") conceptFacade.defineDynamicTaxonomy(defineDynamicTaxonomyRequest2) - DefineLogicalCausationRequest defineLogicalCausationRequest = new DefineLogicalCausationRequest( + DefineTripleSemanticRequest defineLogicalCausationRequest = new DefineTripleSemanticRequest( subjectConceptTypeName: MockSpgTypeNameEnum.DEFAULT_TAXOMOMY_OF_PERSON.getName(), subjectConceptName: "中产阶级", objectConceptTypeName: MockSpgTypeNameEnum.DEFAULT_TAXOMOMY_OF_PERSON.getName(), @@ -139,7 +139,7 @@ class SPGSchemaFacadeTest extends Specification { objectConceptTypeName: MockSpgTypeNameEnum.DEFAULT_TAXOMOMY_OF_PERSON.getName()) conceptFacade.removeDynamicTaxonomy(removeDynamicTaxonomyRequest) - RemoveLogicalCausationRequest removeLogicalCausationRequest = new RemoveLogicalCausationRequest( + RemoveTripleSemanticRequest removeLogicalCausationRequest = new RemoveTripleSemanticRequest( subjectConceptTypeName: MockSpgTypeNameEnum.DEFAULT_TAXOMOMY_OF_PERSON.getName(), subjectConceptName: "中产阶级", objectConceptTypeName: MockSpgTypeNameEnum.DEFAULT_TAXOMOMY_OF_PERSON.getName(), From 56ad88ccd81799143b378c04ca37e343f21b0244 Mon Sep 17 00:00:00 2001 From: matthewhyx Date: Fri, 10 May 2024 21:16:01 +0800 Subject: [PATCH 05/29] feat(schema): support maintain reasoning rule --- .../knext/schema/marklang/concept_rule_ml.py | 12 +++-------- python/knext/knext/schema/rest/concept_api.py | 20 +++++++++---------- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/python/knext/knext/schema/marklang/concept_rule_ml.py b/python/knext/knext/schema/marklang/concept_rule_ml.py index 8c06cebe9..2f8eaa372 100644 --- a/python/knext/knext/schema/marklang/concept_rule_ml.py +++ b/python/knext/knext/schema/marklang/concept_rule_ml.py @@ -9,7 +9,6 @@ # 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. -import os import re from knext.schema import rest @@ -296,7 +295,7 @@ def submit_rule(self): f"Defined reasoning rule for `{self.dst_concept[0]}`/`{self.dst_concept[1]}`" ) else: - self.concept_client.schema_remove_logical_causation_get( + self.concept_client.concept_remove_logical_causation_post( remove_logical_causation_request=rest.RemoveLogicalCausationRequest( subject_concept_type_name="Thing" if len( self.src_concept) == 0 else f"{self.namespace}.{self.src_concept[0]}", @@ -324,7 +323,7 @@ def submit_rule(self): f"Defined belongTo rule for `{self.src_concept[0]}`/`{self.src_concept[1]}`" ) else: - self.concept_client.concept_remove_dynamic_taxonomy_get( + self.concept_client.concept_remove_dynamic_taxonomy_post( remove_dynamic_taxonomy_request=rest.RemoveDynamicTaxonomyRequest( object_concept_type_name=f"{self.namespace}.{self.src_concept[0]}", object_concept_name=self.src_concept[1] @@ -352,7 +351,7 @@ def submit_rule(self): f"`{self.src_concept[0]}`/`{self.src_concept[1]}` -> `{self.dst_concept[0]}`/`{self.dst_concept[1]}`" ) else: - self.concept_client.schema_remove_logical_causation_get( + self.concept_client.concept_remove_logical_causation_post( remove_logical_causation_request=rest.RemoveLogicalCausationRequest( subject_concept_type_name=f"{self.namespace}.{self.src_concept[0]}", subject_concept_name=self.src_concept[1], @@ -423,8 +422,3 @@ def load_script(self, filename): # if rule is the last line of file, then submit it if len(self.rule_text) > 0: self.submit_rule() - - -if __name__ == '__main__': - os.environ["KNEXT_PROJECT_ID"] = "1" - s = SPGConceptRuleMarkLang("/Users/matthew/Downloads/concept.rule") diff --git a/python/knext/knext/schema/rest/concept_api.py b/python/knext/knext/schema/rest/concept_api.py index e70893cc5..403955b56 100644 --- a/python/knext/knext/schema/rest/concept_api.py +++ b/python/knext/knext/schema/rest/concept_api.py @@ -288,12 +288,12 @@ def concept_define_logical_causation_post_with_http_info( collection_formats=collection_formats, ) - def concept_remove_dynamic_taxonomy_get(self, **kwargs): # noqa: E501 + def concept_remove_dynamic_taxonomy_post(self, **kwargs): # noqa: E501 """remove_dynamic_taxonomy # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async_req=True - >>> thread = api.concept_remove_dynamic_taxonomy_get(async_req=True) + >>> thread = api.concept_remove_dynamic_taxonomy_post(async_req=True) >>> result = thread.get() :param async_req bool: execute request asynchronously @@ -310,18 +310,18 @@ def concept_remove_dynamic_taxonomy_get(self, **kwargs): # noqa: E501 returns the request thread. """ kwargs["_return_http_data_only"] = True - return self.concept_remove_dynamic_taxonomy_get_with_http_info( + return self.concept_remove_dynamic_taxonomy_post_with_http_info( **kwargs ) # noqa: E501 - def concept_remove_dynamic_taxonomy_get_with_http_info( + def concept_remove_dynamic_taxonomy_post_with_http_info( self, **kwargs ): # noqa: E501 """remove_dynamic_taxonomy # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async_req=True - >>> thread = api.concept_remove_dynamic_taxonomy_get_with_http_info(async_req=True) + >>> thread = api.concept_remove_dynamic_taxonomy_post_with_http_info(async_req=True) >>> result = thread.get() :param async_req bool: execute request asynchronously @@ -410,12 +410,12 @@ def concept_remove_dynamic_taxonomy_get_with_http_info( collection_formats=collection_formats, ) - def schema_remove_logical_causation_get(self, **kwargs): # noqa: E501 + def concept_remove_logical_causation_post(self, **kwargs): # noqa: E501 """remove_logical_causation # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async_req=True - >>> thread = api.schema_remove_logical_causation_get(async_req=True) + >>> thread = api.concept_remove_logical_causation_post(async_req=True) >>> result = thread.get() :param async_req bool: execute request asynchronously @@ -432,18 +432,18 @@ def schema_remove_logical_causation_get(self, **kwargs): # noqa: E501 returns the request thread. """ kwargs["_return_http_data_only"] = True - return self.schema_remove_logical_causation_get_with_http_info( + return self.concept_remove_logical_causation_post_with_http_info( **kwargs ) # noqa: E501 - def schema_remove_logical_causation_get_with_http_info( + def concept_remove_logical_causation_post_with_http_info( self, **kwargs ): # noqa: E501 """remove_logical_causation # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async_req=True - >>> thread = api.schema_remove_logical_causation_get_with_http_info(async_req=True) + >>> thread = api.concept_remove_logical_causation_post_with_http_info(async_req=True) >>> result = thread.get() :param async_req bool: execute request asynchronously From b754d101d8deaf6db5ed9eb84d90870366420b44 Mon Sep 17 00:00:00 2001 From: kejian Date: Sat, 11 May 2024 10:53:31 +0800 Subject: [PATCH 06/29] fix(schema): fix check dsl syntax --- pom.xml | 5 ++++ server/core/schema/service/pom.xml | 4 ++++ .../semantic/impl/LogicalRuleServiceImpl.java | 24 +++++++++++++++---- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index eda21e0f0..9b86a25f8 100644 --- a/pom.xml +++ b/pom.xml @@ -74,6 +74,11 @@ reasoner-local-runner ${project.version} + + com.antgroup.openspg.reasoner + reasoner-thinker + ${project.version} + com.antgroup.openspg.reasoner reasoner-cloudext-warehouse diff --git a/server/core/schema/service/pom.xml b/server/core/schema/service/pom.xml index 928ab5b15..ab6130686 100644 --- a/server/core/schema/service/pom.xml +++ b/server/core/schema/service/pom.xml @@ -28,6 +28,10 @@ com.antgroup.openspg.reasoner reasoner-local-runner + + com.antgroup.openspg.reasoner + reasoner-thinker + com.antgroup.openspg.server core-schema-model diff --git a/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/semantic/impl/LogicalRuleServiceImpl.java b/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/semantic/impl/LogicalRuleServiceImpl.java index 53c3ab071..f7ef79a95 100644 --- a/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/semantic/impl/LogicalRuleServiceImpl.java +++ b/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/semantic/impl/LogicalRuleServiceImpl.java @@ -19,10 +19,13 @@ import com.antgroup.openspg.core.schema.model.semantic.RuleCode; import com.antgroup.openspg.reasoner.lube.parser.ParserInterface; import com.antgroup.openspg.reasoner.parser.OpenSPGDslParser; +import com.antgroup.openspg.reasoner.thinker.SimplifyThinkerParser; +import com.antgroup.openspg.reasoner.util.Convert2ScalaUtil; import com.antgroup.openspg.server.core.schema.service.semantic.LogicalRuleService; import com.antgroup.openspg.server.core.schema.service.semantic.model.DslCheckResult; import com.antgroup.openspg.server.core.schema.service.semantic.repository.LogicalRuleRepository; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; @@ -82,15 +85,26 @@ public int deleteByRuleId(List ruleCodes) { @Override public DslCheckResult checkDslSyntax(String dsl) { DslCheckResult result = new DslCheckResult(); + if (StringUtils.isEmpty(dsl)) { + result.setPass(false); + result.setErrorPart("empty dsl"); + return result; + } try { ParserInterface parser = new OpenSPGDslParser(); - if (StringUtils.isEmpty(dsl)) { - result.setPass(false); - result.setErrorPart("empty dsl"); - return result; - } parser.parseMultipleStatement(dsl, null).iterator(); return result; + } catch (Exception ex) { + return checkThinkerDslSyntax(dsl); + } + } + + private DslCheckResult checkThinkerDslSyntax(String dsl) { + DslCheckResult result = new DslCheckResult(); + try { + SimplifyThinkerParser parser = new SimplifyThinkerParser(); + parser.parseSimplifyDsl(dsl, Convert2ScalaUtil.toScalaImmutableMap(new HashMap<>())); + return result; } catch (Exception ex) { result.setPass(false); result.setErrorPart(ex.getMessage()); From 467c516443184aac9ce3806c3117d605b0fd6f41 Mon Sep 17 00:00:00 2001 From: matthewhyx Date: Sat, 11 May 2024 18:30:17 +0800 Subject: [PATCH 07/29] feat(schema): support maintain reasoning rule --- .../knext/schema/marklang/concept_rule_ml.py | 41 +++++++++++++++---- .../server/biz/schema/ConceptManager.java | 10 +++++ .../biz/schema/impl/ConceptManagerImpl.java | 11 +++++ .../convertor/ConceptSemanticConvertor.java | 2 +- 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/python/knext/knext/schema/marklang/concept_rule_ml.py b/python/knext/knext/schema/marklang/concept_rule_ml.py index 2f8eaa372..ba57afcf8 100644 --- a/python/knext/knext/schema/marklang/concept_rule_ml.py +++ b/python/knext/knext/schema/marklang/concept_rule_ml.py @@ -39,6 +39,7 @@ class SPGConceptRuleMarkLang: dst_concept = () predicate = None is_reasoning = False + is_priority = False def __init__(self, filename): self.current_line_num = 0 @@ -63,8 +64,22 @@ def parse_concept(self, expression): self.namespace = namespace_match.group(1) return + reasoning_concept_priority_match = re.match( + r"^Priority\s*\(`?([a-zA-Z0-9\.]+)`?\):$", + expression, + ) + if reasoning_concept_priority_match: + assert self.namespace is not None, self.error_msg( + "please define namespace first" + ) + + self.dst_concept = (reasoning_concept_priority_match.group(1), "_root") + self.is_reasoning = True + self.is_priority = True + return + reasoning_concept_match = re.match( - r"^\(`([a-zA-Z0-9\.]+)`/`([^`]+)`\):$", + r"^\(`?([a-zA-Z0-9\.]+)`?/`([^`]+)`\):$", expression, ) if reasoning_concept_match: @@ -77,7 +92,7 @@ def parse_concept(self, expression): return reasoning_po_match = re.match( - r"^\[([^\]]+)\]->\(`([a-zA-Z0-9\.]+)`/`([^`]+)`\):$", + r"^\[([^\]]+)\]->\(`?([a-zA-Z0-9\.]+)`?/`([^`]+)`\):$", expression, ) if reasoning_po_match: @@ -91,7 +106,7 @@ def parse_concept(self, expression): return reasoning_spo_match = re.match( - r"^\(`([a-zA-Z0-9\.]+)`/`([^`]+)`\)-\[([^\]]+)\]->\(`([a-zA-Z0-9\.]+)`/`([^`]+)`\):$", + r"^\(`?([a-zA-Z0-9\.]+)`?/`([^`]+)`\)-\[([^\]]+)\]->\(`([a-zA-Z0-9\.]+)`/`([^`]+)`\):$", expression, ) if reasoning_spo_match: @@ -106,7 +121,7 @@ def parse_concept(self, expression): return type_match = re.match( - r"^`([a-zA-Z0-9\.]+)`/`([^`]+)`:(\s*?([a-zA-Z0-9\.]+)/`([^`]+)`)?$", + r"^`?([a-zA-Z0-9\.]+)`?/`([^`]+)`:(\s*?([a-zA-Z0-9\.]+)/`([^`]+)`)?$", expression, ) if type_match: @@ -184,20 +199,25 @@ def complete_rule(self, rule): break if self.is_reasoning: - if subject_type is None and self.predicate is None: + if subject_type is None and self.predicate is None and not self.is_priority: head = ( - f"Define (o:`{object_type}`/`{object_name}`)" + f"Define ({object_type}/`{object_name}`)" + " {\n" ) elif subject_type is None and self.predicate is not None: head = ( - f"Define [p:{predicate_name}]->(o:`{object_type}`/`{object_name}`)" + f"Define ()-[:{predicate_name}]->(:{object_type}/`{object_name}`)" + + " {\n" + ) + elif self.is_priority: + head = ( + f"DefinePriority ({object_type})" + " {\n" ) else: head = ( - f"Define (s:`{object_type}`/`{object_name}`)-[p:{predicate_name}]->" - f"(o:`{object_type}`/`{object_name}`)" + f"Define (:{object_type}/`{object_name}`)-[:{predicate_name}]->" + f"(:{object_type}/`{object_name}`)" + " {\n" ) elif subject_name is None: @@ -271,6 +291,7 @@ def clear_session(self): self.rule_text = "" self.predicate = None self.is_reasoning = False + self.is_priority = False def submit_rule(self): """ @@ -288,6 +309,7 @@ def submit_rule(self): predicate_name="_conclude" if self.predicate is None else self.predicate, object_concept_type_name=f"{self.namespace}.{self.dst_concept[0]}", object_concept_name=self.dst_concept[1], + semantic_type="REASONING_CONCEPT", dsl=self.rule_text, ) ) @@ -303,6 +325,7 @@ def submit_rule(self): predicate_name="_conclude" if self.predicate is None else self.predicate, object_concept_type_name=f"{self.namespace}.{self.dst_concept[0]}", object_concept_name=self.dst_concept[1], + semantic_type="REASONING_CONCEPT" ) ) print( diff --git a/server/biz/schema/src/main/java/com/antgroup/openspg/server/biz/schema/ConceptManager.java b/server/biz/schema/src/main/java/com/antgroup/openspg/server/biz/schema/ConceptManager.java index 1f78030f2..2cdb1a134 100644 --- a/server/biz/schema/src/main/java/com/antgroup/openspg/server/biz/schema/ConceptManager.java +++ b/server/biz/schema/src/main/java/com/antgroup/openspg/server/biz/schema/ConceptManager.java @@ -13,6 +13,9 @@ package com.antgroup.openspg.server.biz.schema; +import java.util.List; + +import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.core.schema.model.semantic.request.DefineDynamicTaxonomyRequest; import com.antgroup.openspg.core.schema.model.semantic.request.DefineTripleSemanticRequest; import com.antgroup.openspg.core.schema.model.semantic.request.RemoveDynamicTaxonomyRequest; @@ -58,4 +61,11 @@ public interface ConceptManager { * @return list of concept */ ConceptList getConceptDetail(String conceptTypeName, String conceptName); + + /** + * Get reasoning concepts detail. + * @param conceptTypeNames unique names of concept types + * @return list of triple semantic + */ + List getReasoningConceptsDetail(List conceptTypeNames); } diff --git a/server/biz/schema/src/main/java/com/antgroup/openspg/server/biz/schema/impl/ConceptManagerImpl.java b/server/biz/schema/src/main/java/com/antgroup/openspg/server/biz/schema/impl/ConceptManagerImpl.java index 9a126a8c5..03032b986 100644 --- a/server/biz/schema/src/main/java/com/antgroup/openspg/server/biz/schema/impl/ConceptManagerImpl.java +++ b/server/biz/schema/src/main/java/com/antgroup/openspg/server/biz/schema/impl/ConceptManagerImpl.java @@ -121,6 +121,14 @@ public ConceptList getConceptDetail(String conceptTypeName, String conceptName) return new ConceptList(concepts); } + @Override + public List getReasoningConceptsDetail(List conceptTypeNames) { + TripleSemanticQuery query = new TripleSemanticQuery(); + query.setObjectTypeNames(conceptTypeNames); + query.setSpgOntologyEnum(SPGOntologyEnum.REASONING_CONCEPT); + return conceptService.queryTripleSemantic(query); + } + @Override public void defineLogicalCausation(DefineTripleSemanticRequest request) { AssertUtils.assertParamObjectIsNotNull("dsl", request.getDsl()); @@ -142,6 +150,9 @@ public void defineLogicalCausation(DefineTripleSemanticRequest request) { new ConceptIdentifier(request.getObjectConceptName()), logicalRule, StringUtils.isNotBlank(request.getSemanticType()) ? SPGOntologyEnum.valueOf(request.getSemanticType()) : null); + if (conceptSemantic.getOntologyType() != conceptSemantic.getSemanticType()) { + conceptSemantic.setOntologyType(conceptSemantic.getSemanticType()); + } conceptService.upsertTripleSemantic(conceptSemantic); } diff --git a/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/concept/convertor/ConceptSemanticConvertor.java b/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/concept/convertor/ConceptSemanticConvertor.java index d03571768..66d409f3d 100644 --- a/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/concept/convertor/ConceptSemanticConvertor.java +++ b/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/concept/convertor/ConceptSemanticConvertor.java @@ -69,7 +69,7 @@ public static SimpleSemantic convert(DynamicTaxonomySemantic semantic, Long taxo public static SimpleSemantic convert(TripleSemantic semantic) { return new SimpleSemantic( - semantic.getOntologyType() == null ? SPGOntologyEnum.CONCEPT : semantic.getOntologyType(), + semantic.getSemanticType() == null ? SPGOntologyEnum.CONCEPT : semantic.getOntologyType(), semantic.getSubjectIdentifier().getId(), semantic.getObjectIdentifier().getId(), semantic.getPredicateIdentifier(), From 596ee97e06a71578b4b1ecff184c27fc96f508cf Mon Sep 17 00:00:00 2001 From: matthewhyx Date: Mon, 13 May 2024 11:44:32 +0800 Subject: [PATCH 08/29] feat(schema): support maintain reasoning rule --- .../api/facade/client/ConceptFacade.java | 11 ++++++++++ .../dto/schema/request/SPGTypeRequest.java | 17 ++++++++++++++ .../api/http/client/HttpConceptFacade.java | 9 ++++++++ .../forest/client/ConceptForestClient.java | 7 ++++++ .../server/openapi/ConceptController.java | 22 +++++++++++++++++++ 5 files changed, 66 insertions(+) diff --git a/server/api/facade/src/main/java/com/antgroup/openspg/server/api/facade/client/ConceptFacade.java b/server/api/facade/src/main/java/com/antgroup/openspg/server/api/facade/client/ConceptFacade.java index 6b9b27337..8d0c152cf 100644 --- a/server/api/facade/src/main/java/com/antgroup/openspg/server/api/facade/client/ConceptFacade.java +++ b/server/api/facade/src/main/java/com/antgroup/openspg/server/api/facade/client/ConceptFacade.java @@ -13,6 +13,9 @@ package com.antgroup.openspg.server.api.facade.client; +import java.util.List; + +import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.core.schema.model.semantic.request.DefineDynamicTaxonomyRequest; import com.antgroup.openspg.core.schema.model.semantic.request.DefineTripleSemanticRequest; import com.antgroup.openspg.core.schema.model.semantic.request.RemoveDynamicTaxonomyRequest; @@ -20,6 +23,7 @@ import com.antgroup.openspg.core.schema.model.type.ConceptList; import com.antgroup.openspg.server.api.facade.ApiResponse; import com.antgroup.openspg.server.api.facade.dto.schema.request.ConceptRequest; +import com.antgroup.openspg.server.api.facade.dto.schema.request.SPGTypeRequest; /** * The interface to query concepts that defined under a concept type, also provides method to define @@ -38,6 +42,13 @@ public interface ConceptFacade { */ ApiResponse queryConcept(ConceptRequest request); + /** + * Query reasoning concepts detail by the concept types, the interface returned triple semantic object for rule detail. + * @param request + * @return + */ + ApiResponse> getReasoningConceptsDetail(SPGTypeRequest request); + /** * Define dynamic taxonomy rule between an entity and a concept, Currently the taxonomic predicate * is belongTo. diff --git a/server/api/facade/src/main/java/com/antgroup/openspg/server/api/facade/dto/schema/request/SPGTypeRequest.java b/server/api/facade/src/main/java/com/antgroup/openspg/server/api/facade/dto/schema/request/SPGTypeRequest.java index ee44a6bfe..3f02d6b90 100644 --- a/server/api/facade/src/main/java/com/antgroup/openspg/server/api/facade/dto/schema/request/SPGTypeRequest.java +++ b/server/api/facade/src/main/java/com/antgroup/openspg/server/api/facade/dto/schema/request/SPGTypeRequest.java @@ -13,7 +13,11 @@ package com.antgroup.openspg.server.api.facade.dto.schema.request; +import java.util.List; + import com.antgroup.openspg.server.common.model.base.BaseRequest; +import com.google.common.collect.Lists; +import org.apache.commons.collections4.CollectionUtils; /** Query schema type. */ public class SPGTypeRequest extends BaseRequest { @@ -23,10 +27,19 @@ public class SPGTypeRequest extends BaseRequest { /** The unique name of entity to query. */ private String name; + private List lstName; + public String getName() { return name; } + public List getNameList() { + if (CollectionUtils.isEmpty(lstName)) { + lstName = Lists.newArrayList(name); + } + return lstName; + } + public SPGTypeRequest setName(String name) { this.name = name; return this; @@ -38,6 +51,10 @@ public SPGTypeRequest(String name) { this.name = name; } + public SPGTypeRequest(List lst) { + this.lstName = lst; + } + @Override public String toString() { return name; diff --git a/server/api/http-client/src/main/java/com/antgroup/openspg/server/api/http/client/HttpConceptFacade.java b/server/api/http-client/src/main/java/com/antgroup/openspg/server/api/http/client/HttpConceptFacade.java index 573ca8a01..e4dfddd6a 100644 --- a/server/api/http-client/src/main/java/com/antgroup/openspg/server/api/http/client/HttpConceptFacade.java +++ b/server/api/http-client/src/main/java/com/antgroup/openspg/server/api/http/client/HttpConceptFacade.java @@ -13,6 +13,9 @@ package com.antgroup.openspg.server.api.http.client; +import java.util.List; + +import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.core.schema.model.semantic.request.DefineDynamicTaxonomyRequest; import com.antgroup.openspg.core.schema.model.semantic.request.DefineTripleSemanticRequest; import com.antgroup.openspg.core.schema.model.semantic.request.RemoveDynamicTaxonomyRequest; @@ -21,6 +24,7 @@ import com.antgroup.openspg.server.api.facade.ApiResponse; import com.antgroup.openspg.server.api.facade.client.ConceptFacade; import com.antgroup.openspg.server.api.facade.dto.schema.request.ConceptRequest; +import com.antgroup.openspg.server.api.facade.dto.schema.request.SPGTypeRequest; import com.antgroup.openspg.server.api.http.client.forest.ForestUtils; import com.antgroup.openspg.server.api.http.client.forest.client.ConceptForestClient; @@ -31,6 +35,11 @@ public ApiResponse queryConcept(ConceptRequest request) { return ForestUtils.call(ConceptForestClient.class, c -> c.queryConcept(request)); } + @Override + public ApiResponse> getReasoningConceptsDetail(SPGTypeRequest request) { + return ForestUtils.call(ConceptForestClient.class, c -> c.getReasoningConcept(request)); + } + @Override public ApiResponse defineDynamicTaxonomy(DefineDynamicTaxonomyRequest request) { return ForestUtils.call(ConceptForestClient.class, c -> c.defineDynamicTaxonomy(request)); diff --git a/server/api/http-client/src/main/java/com/antgroup/openspg/server/api/http/client/forest/client/ConceptForestClient.java b/server/api/http-client/src/main/java/com/antgroup/openspg/server/api/http/client/forest/client/ConceptForestClient.java index 25d069ca6..0283ed81a 100644 --- a/server/api/http-client/src/main/java/com/antgroup/openspg/server/api/http/client/forest/client/ConceptForestClient.java +++ b/server/api/http-client/src/main/java/com/antgroup/openspg/server/api/http/client/forest/client/ConceptForestClient.java @@ -13,12 +13,16 @@ package com.antgroup.openspg.server.api.http.client.forest.client; +import java.util.List; + +import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.core.schema.model.semantic.request.DefineDynamicTaxonomyRequest; import com.antgroup.openspg.core.schema.model.semantic.request.DefineTripleSemanticRequest; import com.antgroup.openspg.core.schema.model.semantic.request.RemoveDynamicTaxonomyRequest; import com.antgroup.openspg.core.schema.model.semantic.request.RemoveTripleSemanticRequest; import com.antgroup.openspg.core.schema.model.type.ConceptList; import com.antgroup.openspg.server.api.facade.dto.schema.request.ConceptRequest; +import com.antgroup.openspg.server.api.facade.dto.schema.request.SPGTypeRequest; import com.antgroup.openspg.server.api.http.client.util.HttpClientConstants; import com.dtflys.forest.annotation.Address; import com.dtflys.forest.annotation.BodyType; @@ -38,6 +42,9 @@ public interface ConceptForestClient { @Get(value = "/public/v1/concept/queryConcept") ForestResponse queryConcept(@Query ConceptRequest request); + @Get(value = "/public/v1/concept/getReasoningConcept") + ForestResponse> getReasoningConcept(@Query SPGTypeRequest request); + @Post(value = "/public/v1/concept/defineDynamicTaxonomy") ForestResponse defineDynamicTaxonomy(@JSONBody DefineDynamicTaxonomyRequest request); diff --git a/server/api/http-server/src/main/java/com/antgroup/openspg/server/api/http/server/openapi/ConceptController.java b/server/api/http-server/src/main/java/com/antgroup/openspg/server/api/http/server/openapi/ConceptController.java index e2cd9cc19..153d5fa16 100644 --- a/server/api/http-server/src/main/java/com/antgroup/openspg/server/api/http/server/openapi/ConceptController.java +++ b/server/api/http-server/src/main/java/com/antgroup/openspg/server/api/http/server/openapi/ConceptController.java @@ -13,12 +13,16 @@ package com.antgroup.openspg.server.api.http.server.openapi; +import java.util.List; + +import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.core.schema.model.semantic.request.DefineDynamicTaxonomyRequest; import com.antgroup.openspg.core.schema.model.semantic.request.DefineTripleSemanticRequest; import com.antgroup.openspg.core.schema.model.semantic.request.RemoveDynamicTaxonomyRequest; import com.antgroup.openspg.core.schema.model.semantic.request.RemoveTripleSemanticRequest; import com.antgroup.openspg.core.schema.model.type.ConceptList; import com.antgroup.openspg.server.api.facade.dto.schema.request.ConceptRequest; +import com.antgroup.openspg.server.api.facade.dto.schema.request.SPGTypeRequest; import com.antgroup.openspg.server.api.http.server.BaseController; import com.antgroup.openspg.server.api.http.server.HttpBizCallback; import com.antgroup.openspg.server.api.http.server.HttpBizTemplate; @@ -57,6 +61,24 @@ public ConceptList action() { }); } + @RequestMapping(value = "/getReasoningConcept", method = RequestMethod.GET) + @ResponseBody + public ResponseEntity getReasoningConcept(SPGTypeRequest request) { + return HttpBizTemplate.execute( + new HttpBizCallback>() { + @Override + public void check() { + AssertUtils.assertParamObjectIsNotNull("request", request); + AssertUtils.assertParamObjectIsNotNull("conceptTypeName", request.getNameList()); + } + + @Override + public List action() { + return conceptManager.getReasoningConceptsDetail(request.getNameList()); + } + }); + } + @RequestMapping(value = "/defineDynamicTaxonomy", method = RequestMethod.POST) @ResponseBody public ResponseEntity defineDynamicTaxonomy( From 16362b39eead0f6103c8bbcfd141599c7b9a8cb4 Mon Sep 17 00:00:00 2001 From: wenchengyao <45688588+ChengyaoWen@users.noreply.github.com> Date: Thu, 16 May 2024 17:57:47 +0800 Subject: [PATCH 09/29] fix(reasoner): fix findAllTypesAllowIsolateVertex (#249) --- .../openspg/reasoner/parser/OpenSPGDslParser.scala | 2 +- .../openspg/reasoner/parser/OpenSPGDslParserTest.scala | 10 ++++++++++ .../antgroup/openspg/reasoner/util/LoaderUtil.scala | 8 ++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/reasoner/kgdsl-parser/src/main/scala/com/antgroup/openspg/reasoner/parser/OpenSPGDslParser.scala b/reasoner/kgdsl-parser/src/main/scala/com/antgroup/openspg/reasoner/parser/OpenSPGDslParser.scala index e5afb5c1f..3012a23d8 100644 --- a/reasoner/kgdsl-parser/src/main/scala/com/antgroup/openspg/reasoner/parser/OpenSPGDslParser.scala +++ b/reasoner/kgdsl-parser/src/main/scala/com/antgroup/openspg/reasoner/parser/OpenSPGDslParser.scala @@ -85,7 +85,7 @@ class OpenSPGDslParser extends ParserInterface { */ override def parse(text: String): Block = { val blocks = parseMultipleStatement(text) - if (blocks.size > 1) { + if (blocks.size != 1) { throw KGDSLOneTaskException("dsl = " + text + "blocks num is " + blocks.size) } blocks.head diff --git a/reasoner/kgdsl-parser/src/test/scala/com/antgroup/openspg/reasoner/parser/OpenSPGDslParserTest.scala b/reasoner/kgdsl-parser/src/test/scala/com/antgroup/openspg/reasoner/parser/OpenSPGDslParserTest.scala index 62cb509ff..af6a127a8 100644 --- a/reasoner/kgdsl-parser/src/test/scala/com/antgroup/openspg/reasoner/parser/OpenSPGDslParserTest.scala +++ b/reasoner/kgdsl-parser/src/test/scala/com/antgroup/openspg/reasoner/parser/OpenSPGDslParserTest.scala @@ -184,6 +184,16 @@ class OpenSPGDslParserTest extends AnyFunSpec { } } + it("test exception2") { + val dsl = """test""".stripMargin + try { + parser.parse(dsl) + } catch { + case ex: KGDSLOneTaskException => + ex.getMessage.contains("num is 0") should equal(true) + } + } + it("opChainTest") { val chain = OpChainExpr(Filter(BinaryOpExpr(BNotEqual, Ref("a"), Ref("b"))), null) diff --git a/reasoner/runner/runner-common/src/main/scala/com/antgroup/openspg/reasoner/util/LoaderUtil.scala b/reasoner/runner/runner-common/src/main/scala/com/antgroup/openspg/reasoner/util/LoaderUtil.scala index f2e4eeaf0..525ae2189 100644 --- a/reasoner/runner/runner-common/src/main/scala/com/antgroup/openspg/reasoner/util/LoaderUtil.scala +++ b/reasoner/runner/runner-common/src/main/scala/com/antgroup/openspg/reasoner/util/LoaderUtil.scala @@ -67,6 +67,14 @@ object LoaderUtil { private def findAllTypesAllowIsolateVertex(logicalPlan: LogicalOperator): Set[String] = { logicalPlan.transform[Set[String]] { + case (PatternScan(_, pattern), typeSets) => + // 如果PatternScan只匹配单个起点,也需要加载孤点 + val connectionSet = pattern.topology.get(pattern.root.alias) + if (connectionSet.isEmpty || connectionSet.get.isEmpty) { + typeSets.flatten.toSet ++ pattern.root.typeNames + } else { + typeSets.flatten.toSet + } case (LinkedExpand(_, linkedEdgePattern), typeSets) => typeSets.flatten.toSet ++ linkedEdgePattern.src.typeNames case (_, typeSets) => typeSets.flatten.toSet From 85c0187c48ac9a1fcca82b3b3fd71fb8c0456bd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=9F=B9=E9=BE=99?= Date: Tue, 21 May 2024 15:08:02 +0800 Subject: [PATCH 10/29] feat(reasoner): support trace execution info (#237) Co-authored-by: wenchengyao --- .../semantic/rules/MetaConceptExplain.scala | 20 +++--- .../runner/local/LocalReasonerRunner.java | 4 +- .../local/model/LocalReasonerResult.java | 19 +++++- .../reasoner/runner/local/rdg/LocalRDG.java | 66 ++++++++++++++++--- .../reasoner/runner/local/rdg/LocalRow.java | 3 +- .../runner/local/LocalReasonerResultTest.java | 2 +- .../recorder/action/DebugInfoWithStartId.java | 21 ++++-- .../warehouse/utils/WareHouseUtils.java | 10 +++ 8 files changed, 112 insertions(+), 33 deletions(-) diff --git a/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/validate/semantic/rules/MetaConceptExplain.scala b/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/validate/semantic/rules/MetaConceptExplain.scala index bb0582342..ae95e68b6 100644 --- a/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/validate/semantic/rules/MetaConceptExplain.scala +++ b/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/validate/semantic/rules/MetaConceptExplain.scala @@ -14,9 +14,10 @@ package com.antgroup.openspg.reasoner.lube.logical.validate.semantic.rules import com.antgroup.openspg.reasoner.lube.block.{Block, MatchBlock} -import com.antgroup.openspg.reasoner.lube.catalog.{SemanticPropertyGraph, SemanticRule} +import com.antgroup.openspg.reasoner.lube.catalog.SemanticPropertyGraph import com.antgroup.openspg.reasoner.lube.common.pattern.{ Connection, + EntityElement, GraphPattern, PatternElement } @@ -33,9 +34,9 @@ object MetaConceptExplain extends Explain { } else { val newPatterns = patterns.map { p => val pattern = p._2.graphPattern - val metaConceptEdges = - pattern.edges.values.filter(edge => - edge.exists(_.relTypes.contains("belongTo")) && !edge.exists(_.target.contains('/'))) + val metaConceptEdges = pattern.edges.values.filter(edge => + edge.exists(_.relTypes.contains("belongTo")) && !edge.exists(e => + pattern.nodes(e.target).isInstanceOf[EntityElement])) if (metaConceptEdges.isEmpty) { p } else { @@ -44,11 +45,8 @@ object MetaConceptExplain extends Explain { metaConceptEdges.foreach(e => parseMetaConcept(kg, e, pattern, metaConceptMap)) val newNodes = pattern.nodes.map(n => if (metaConceptMap.contains(n._1)) { - n.copy( - n._1, - PatternElement(n._1, metaConceptMap(n._1), n._2.rule))} - else n - ) + n.copy(n._1, PatternElement(n._1, metaConceptMap(n._1), n._2.rule)) + } else n) (p._1, p._2.copy(graphPattern = pattern.copy(nodes = newNodes))) } } @@ -72,8 +70,8 @@ object MetaConceptExplain extends Explain { if (!metaConceptMap.contains(targetAlias)) { metaConceptMap(targetAlias) = Set.empty } - val metaConcepts = metaConceptMap(targetAlias).++(matchedRules.map(r => - r.split("_belongTo_").last)) + val metaConcepts = + metaConceptMap(targetAlias).++(matchedRules.map(r => r.split("_belongTo_").last)) metaConceptMap.put(targetAlias, metaConcepts) } } diff --git a/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/LocalReasonerRunner.java b/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/LocalReasonerRunner.java index 9dfb93e84..a0f3157a7 100644 --- a/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/LocalReasonerRunner.java +++ b/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/LocalReasonerRunner.java @@ -174,9 +174,7 @@ private LocalReasonerResult doRun(LocalReasonerTask task) { mockLocalGraphLoader.load(); } - if (task.getParams().containsKey(ConfigKey.KG_REASONER_DEBUG_TRACE_ENABLE)) { - task.setExecutionRecorder(new DefaultRecorder()); - } + task.setExecutionRecorder(new DefaultRecorder()); if (physicalOpRoot instanceof Select) { LocalRow row = (LocalRow) ((Select) physicalOpRoot).row(); diff --git a/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/model/LocalReasonerResult.java b/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/model/LocalReasonerResult.java index 00e61f521..195493050 100644 --- a/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/model/LocalReasonerResult.java +++ b/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/model/LocalReasonerResult.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; import org.apache.commons.collections4.CollectionUtils; public class LocalReasonerResult { @@ -30,6 +31,8 @@ public class LocalReasonerResult { private final String debugTraceInfo; + private List> debugWithStartNodeInfos; + // select result private final List columns; private final List rows; @@ -68,7 +71,8 @@ public LocalReasonerResult( List> vertexList, List> edgeList, boolean graphResult, - String debugTraceInfo) { + String debugTraceInfo, + List> debugWithStartNodeInfos) { this.columns = null; this.rows = null; this.graphResult = graphResult; @@ -76,6 +80,7 @@ public LocalReasonerResult( this.edgeList = edgeList; this.errMsg = ""; this.debugTraceInfo = debugTraceInfo; + this.debugWithStartNodeInfos = debugWithStartNodeInfos; } /** output graph and row */ @@ -85,7 +90,8 @@ public LocalReasonerResult( List> vertexList, List> edgeList, boolean graphResult, - String debugTraceInfo) { + String debugTraceInfo, + List> debugWithStartNodeInfos) { this.columns = columns; this.rows = rows; this.graphResult = graphResult; @@ -93,6 +99,15 @@ public LocalReasonerResult( this.edgeList = edgeList; this.errMsg = ""; this.debugTraceInfo = debugTraceInfo; + this.debugWithStartNodeInfos = debugWithStartNodeInfos; + } + + public void setDebugWithStartNodeInfos(List> debugWithStartNodeInfos) { + this.debugWithStartNodeInfos = debugWithStartNodeInfos; + } + + public List> getDebugWithStartNodeInfos() { + return debugWithStartNodeInfos; } /** diff --git a/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/rdg/LocalRDG.java b/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/rdg/LocalRDG.java index e84297541..0ac76b1d5 100644 --- a/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/rdg/LocalRDG.java +++ b/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/rdg/LocalRDG.java @@ -14,6 +14,7 @@ package com.antgroup.openspg.reasoner.runner.local.rdg; import com.alibaba.fastjson.JSON; +import com.antgroup.openspg.common.util.StringUtils; import com.antgroup.openspg.reasoner.common.Utils; import com.antgroup.openspg.reasoner.common.constants.Constants; import com.antgroup.openspg.reasoner.common.exception.NotImplementedException; @@ -124,6 +125,8 @@ public class LocalRDG extends RDG { private final String startVertexAlias; + private String curRdgStartVertexAlias; + /** kg graph and it's schema */ private java.util.List> kgGraphList = new ArrayList<>(); @@ -165,7 +168,11 @@ private java.util.Set getStartId(java.util.List> k java.util.Set startIdSet = new HashSet<>(); for (KgGraph kgGraph : kgGraphList) { java.util.List> startVertexList = - kgGraph.getVertex(this.startVertexAlias); + kgGraph.getVertex(this.curRdgStartVertexAlias); + for (IVertex startId : startVertexList) { + startIdSet.add(startId.getId()); + } + startVertexList = kgGraph.getVertex(this.startVertexAlias); for (IVertex startId : startVertexList) { startIdSet.add(startId.getId()); } @@ -230,6 +237,7 @@ public LocalRDG( @Override public LocalRDG patternScan(Pattern pattern) { long startTime = System.currentTimeMillis(); + this.curRdgStartVertexAlias = WareHouseUtils.getPatternScanRootAlias(pattern); java.util.List rootVertexRuleList = WareHouseUtils.getVertexRuleList(pattern); java.util.Map> dstVertexRuleMap = WareHouseUtils.getDstVertexRuleList(pattern); @@ -1067,13 +1075,18 @@ private void addProperty(String alias, Field property, Boolean isLast) { + property.toString()); } - private java.util.Map getProcessInfo(DebugInfoWithStartId startId, String bizId) { + private java.util.Map getProcessInfo( + DebugInfoWithStartId startId, String bizId, String targetLabel, String targetId) { java.util.Map processInfo = startId.toJsonObj(); + if (StringUtils.isNotBlank(targetLabel)) { + processInfo.put("targetLabel", targetLabel.split("/")[0]); + processInfo.put("targetId", targetId); + } java.util.Map startIdInfo = new HashMap<>(); startIdInfo.put("bizId", bizId); startIdInfo.put("label", startId.getVertexId().getType()); startIdInfo.put("id", startId.getVertexId()); - processInfo.put("start_node", startIdInfo); + processInfo.put("startNode", startIdInfo); return processInfo; } @@ -1091,16 +1104,24 @@ private void addRelation(AddPredicate addPredicate) { edge.getValue() .put( - "__process__", - getProcessInfo(startId, (String) edge.getValue().get(Constants.EDGE_FROM_ID_KEY))); + "process", + getProcessInfo( + startId, + (String) edge.getValue().get(Constants.EDGE_FROM_ID_KEY), + (String) edge.getValue().get(Constants.EDGE_TO_ID_TYPE_KEY), + (String) edge.getValue().get(Constants.EDGE_TO_ID_KEY))); } if (debugInfoMap.containsKey(edge.getTargetId())) { DebugInfoWithStartId startId = debugInfoMap.get(edge.getTargetId()); edge.getValue() .put( - "__process__", - getProcessInfo(startId, (String) edge.getValue().get(Constants.EDGE_TO_ID_KEY))); + "process", + getProcessInfo( + startId, + (String) edge.getValue().get(Constants.EDGE_TO_ID_KEY), + (String) edge.getValue().get(Constants.EDGE_FROM_ID_TYPE_KEY), + (String) edge.getValue().get(Constants.EDGE_FROM_ID_KEY))); } // add to result list this.resultEdgeSet.add(edge); @@ -1351,23 +1372,50 @@ public PartialGraphPattern getKgGraphSchema() { return this.kgGraphSchema; } + private java.util.List> generateStartNodeDebugInfo() { + java.util.Map debugInfoMap = + this.executionRecorder.getCurStageDebugInfo(); + java.util.List> debugStartNodeINfos = new ArrayList<>(); + for (IVertexId id : debugInfoMap.keySet()) { + DebugInfoWithStartId debugInfoWithStartId = debugInfoMap.get(id); + IVertex vertex = + graphState.getVertex(debugInfoWithStartId.getVertexId(), null); + if (vertex == null) { + continue; + } + debugStartNodeINfos.add( + getProcessInfo( + debugInfoWithStartId, + vertex.getValue().get(Constants.NODE_ID_KEY).toString(), + "", + "")); + } + return debugStartNodeINfos; + } /** get ddl result */ public LocalReasonerResult getResult() { + java.util.List> debugStartNodeINfos = + generateStartNodeDebugInfo(); return new LocalReasonerResult( Lists.newArrayList(resultVertexSet), Lists.newArrayList(resultEdgeSet), true, - this.executionRecorder.toReadableString()); + this.executionRecorder.toReadableString(), + debugStartNodeINfos); } /** add graph to traversal graph */ private LocalReasonerResult getRDGGraph() { if (!isCarryTraversalGraph) { + java.util.List> debugStartNodeINfos = + generateStartNodeDebugInfo(); + return new LocalReasonerResult( Lists.newArrayList(), Lists.newArrayList(), false, - this.executionRecorder.toReadableString()); + this.executionRecorder.toReadableString(), + debugStartNodeINfos); } LocalReasonerResult localReasonerResult = getResult(); this.kgGraphList.forEach( diff --git a/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/rdg/LocalRow.java b/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/rdg/LocalRow.java index 99445ec32..2679d66c4 100644 --- a/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/rdg/LocalRow.java +++ b/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/rdg/LocalRow.java @@ -73,6 +73,7 @@ public LocalReasonerResult getResult() { graphRst.getVertexList(), graphRst.getEdgeList(), graphRst.isGraphResult(), - graphRst.getDebugTraceInfo()); + graphRst.getDebugTraceInfo(), + graphRst.getDebugWithStartNodeInfos()); } } diff --git a/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/LocalReasonerResultTest.java b/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/LocalReasonerResultTest.java index e2f3d1bfe..6c8514e93 100644 --- a/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/LocalReasonerResultTest.java +++ b/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/LocalReasonerResultTest.java @@ -45,7 +45,7 @@ public void localReasonerResultTest() { Direction.OUT)); boolean ddlResult = true; LocalReasonerResult localReasonerResult = - new LocalReasonerResult(vertexList, edgeList, ddlResult, ""); + new LocalReasonerResult(vertexList, edgeList, ddlResult, "", Lists.newArrayList()); Assert.assertTrue(localReasonerResult.isGraphResult()); System.out.println(localReasonerResult); diff --git a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/DebugInfoWithStartId.java b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/DebugInfoWithStartId.java index bedfeea7b..e72fe6502 100644 --- a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/DebugInfoWithStartId.java +++ b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/DebugInfoWithStartId.java @@ -36,16 +36,25 @@ public IVertexId getVertexId() { public Map toJsonObj() { Map result = new HashMap<>(); - Map hitRuleInfo = new HashMap<>(); + List> hitRuleInfos = new ArrayList<>(); + for (Rule r : hitRules) { - hitRuleInfo.put(r.getName(), StringUtils.join(WareHouseUtils.getRuleList(r), ",")); + Map hitRuleInfoDetail = new HashMap<>(); + hitRuleInfoDetail.put("ruleName", r.getName()); + hitRuleInfoDetail.put("ruleValue", StringUtils.join(WareHouseUtils.getRuleList(r), ",")); + hitRuleInfoDetail.put("hitValue", ""); + hitRuleInfos.add(hitRuleInfoDetail); } - result.put("hit_rule", hitRuleInfo); - Map failedRuleInfo = new HashMap<>(); + result.put("hitRule", hitRuleInfos); + List> failedRuleInfos = new ArrayList<>(); for (Rule r : failedRules) { - failedRuleInfo.put(r.getName(), StringUtils.join(WareHouseUtils.getRuleList(r), ",")); + Map failedRuleInfoDetail = new HashMap<>(); + failedRuleInfoDetail.put("ruleName", r.getName()); + failedRuleInfoDetail.put("ruleValue", StringUtils.join(WareHouseUtils.getRuleList(r), ",")); + failedRuleInfoDetail.put("hitValue", ""); + failedRuleInfos.add(failedRuleInfoDetail); } - result.put("failed_rule", failedRuleInfo); + result.put("failedRule", failedRuleInfos); return result; } 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 bc73cd239..569d1f5c5 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 @@ -71,6 +71,16 @@ public static List getPatternRuleList(Pattern pattern) { return patternRuleList; } + /** + * get start vertex alias name + * + * @param pattern + * @return + */ + public static String getPatternScanRootAlias(Pattern pattern) { + return pattern.root().alias(); + } + /** * get vertex rule string * From f942360dd4e1b2894c0deec5b6249603ac23eb07 Mon Sep 17 00:00:00 2001 From: FishJoy Date: Wed, 12 Jun 2024 16:48:02 +0800 Subject: [PATCH 11/29] feat(reasoner): support spo inference (#280) Co-authored-by: kejian --- .../openspg/reasoner/thinker/Thinker.java | 3 +- .../thinker/engine/DefaultThinker.java | 3 +- .../reasoner/thinker/engine/Graph.java | 4 +- .../reasoner/thinker/engine/GraphStore.java | 34 +- .../reasoner/thinker/engine/InfGraph.java | 307 ++++++++++++++++-- .../thinker/engine/MemTripleStore.java | 33 +- .../reasoner/thinker/logic/Evidence.java | 27 ++ .../reasoner/thinker/logic/LogicNetwork.java | 39 ++- .../reasoner/thinker/logic/graph/Element.java | 18 + .../reasoner/thinker/logic/graph/Entity.java | 35 +- .../reasoner/thinker/logic/graph/Node.java | 25 ++ .../thinker/logic/graph/Predicate.java | 29 ++ .../reasoner/thinker/logic/graph/Triple.java | 33 ++ .../reasoner/thinker/logic/graph/Value.java | 72 +++- .../thinker/logic/rule/TreeLogger.java | 13 +- .../logic/rule/exact/QlExpressCondition.java | 87 ++++- .../logic/rule/visitor/RuleExecutor.java | 9 +- .../thinker/qlexpress/QlExpressRunner.java | 121 +++++++ .../op/RichOperatorEqualsLessMore.java | 144 ++++++++ .../reasoner/thinker/ThinkerRuleParser.scala | 19 +- .../reasoner/thinker/DefaultThinkerTests.java | 109 ++----- .../openspg/reasoner/thinker/GraphUtil.java | 81 +++++ .../reasoner/thinker/HypertensionTest.java | 73 +++++ .../reasoner/thinker/InsuranceTests.java | 124 +++++++ .../openspg/reasoner/thinker/MedTests.java | 90 +++++ .../thinker/catalog/ResourceLogicCatalog.java | 90 +++++ .../thinker/logic/rule/QlExpressTest.java | 83 +++++ .../thinker/logic/rule/RuleExecutorTests.java | 13 - .../src/test/resources/Hypertension.txt | 63 ++++ .../src/test/resources/InsuranceRules.txt | 16 + .../thinker/src/test/resources/Medical.txt | 27 ++ .../thinker/SimplifyThinkerParserTest.scala | 30 +- .../openspg/reasoner/udf/rule/RuleRunner.java | 22 +- .../udf/rule/op/OperatorEqualsLessMore.java | 14 +- .../reasoner/udf/rule/op/OperatorGetSPO.java | 36 ++ 35 files changed, 1716 insertions(+), 210 deletions(-) create mode 100644 reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/Evidence.java create mode 100644 reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/qlexpress/QlExpressRunner.java create mode 100644 reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/qlexpress/op/RichOperatorEqualsLessMore.java create mode 100644 reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/GraphUtil.java create mode 100644 reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/HypertensionTest.java create mode 100644 reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/InsuranceTests.java create mode 100644 reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/MedTests.java create mode 100644 reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/catalog/ResourceLogicCatalog.java create mode 100644 reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/logic/rule/QlExpressTest.java create mode 100644 reasoner/thinker/src/test/resources/Hypertension.txt create mode 100644 reasoner/thinker/src/test/resources/InsuranceRules.txt create mode 100644 reasoner/thinker/src/test/resources/Medical.txt create mode 100644 reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/rule/op/OperatorGetSPO.java diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/Thinker.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/Thinker.java index 061106c90..492787d0a 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/Thinker.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/Thinker.java @@ -15,6 +15,7 @@ import com.antgroup.openspg.reasoner.thinker.logic.Result; import com.antgroup.openspg.reasoner.thinker.logic.graph.Element; +import com.antgroup.openspg.reasoner.thinker.logic.graph.Node; import java.util.List; import java.util.Map; @@ -25,5 +26,5 @@ public interface Thinker { List find(Element s, Element p, Element o, Map context); - List find(Element target, Map context); + List find(Node target, Map context); } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/DefaultThinker.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/DefaultThinker.java index 767bdb740..1954164f3 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/DefaultThinker.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/DefaultThinker.java @@ -19,6 +19,7 @@ import com.antgroup.openspg.reasoner.thinker.catalog.LogicCatalog; import com.antgroup.openspg.reasoner.thinker.logic.Result; import com.antgroup.openspg.reasoner.thinker.logic.graph.Element; +import com.antgroup.openspg.reasoner.thinker.logic.graph.Node; import com.antgroup.openspg.reasoner.thinker.logic.graph.Triple; import java.util.*; @@ -50,7 +51,7 @@ public List find(Element s, Element p, Element o, Map co } @Override - public List find(Element s, Map context) { + public List find(Node s, Map context) { this.infGraph.clear(); return this.infGraph.find(s, context); } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/Graph.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/Graph.java index d83fea9ba..670adbd01 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/Graph.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/Graph.java @@ -14,7 +14,7 @@ package com.antgroup.openspg.reasoner.thinker.engine; import com.antgroup.openspg.reasoner.thinker.logic.Result; -import com.antgroup.openspg.reasoner.thinker.logic.graph.Element; +import com.antgroup.openspg.reasoner.thinker.logic.graph.Node; import com.antgroup.openspg.reasoner.thinker.logic.graph.Triple; import java.util.List; import java.util.Map; @@ -24,5 +24,5 @@ public interface Graph { List find(Triple pattern, Map context); - List find(Element s, Map context); + List find(Node s, Map context); } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/GraphStore.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/GraphStore.java index 5e453564f..a0b6a6945 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/GraphStore.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/GraphStore.java @@ -22,10 +22,7 @@ import com.antgroup.openspg.reasoner.graphstate.GraphState; import com.antgroup.openspg.reasoner.thinker.logic.Result; import com.antgroup.openspg.reasoner.thinker.logic.graph.*; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; +import java.util.*; public class GraphStore implements Graph { private GraphState graphState; @@ -45,14 +42,16 @@ public List find(Triple pattern, Map context) { data = getTriple((Entity) pattern.getSubject(), Direction.OUT); } else if (pattern.getObject() instanceof Entity) { data = getTriple((Entity) pattern.getObject(), Direction.IN); + } else if (pattern.getSubject() instanceof Triple) { + data = getTriple((Triple) pattern.getSubject(), (Predicate) pattern.getPredicate()); } else { - throw new RuntimeException("Cannot support " + pattern); + return new ArrayList<>(); } return matchInGraph(pattern, data); } @Override - public List find(Element s, Map context) { + public List find(Node s, Map context) { return Collections.emptyList(); } @@ -61,8 +60,11 @@ protected List getTriple(Entity s, Direction direction) { if (direction == Direction.OUT) { IVertex vertex = this.graphState.getVertex(IVertexId.from(s.getId(), s.getType()), null); + if (vertex == null) { + return triples; + } for (String key : vertex.getValue().getKeySet()) { - triples.add(new Triple(s, new Predicate(key), new Value(key, vertex.getValue().get(key)))); + triples.add(new Triple(s, new Predicate(key), new Value(vertex.getValue().get(key)))); } } List> edges = @@ -74,6 +76,24 @@ protected List getTriple(Entity s, Direction direction) { return triples; } + protected List getTriple(Triple triple, Predicate predicate) { + List triples = new LinkedList<>(); + Entity s = (Entity) triple.getSubject(); + Predicate p = (Predicate) triple.getPredicate(); + Entity o = (Entity) triple.getObject(); + List> edges = + this.graphState.getEdges( + IVertexId.from(s.getId(), s.getType()), null, null, null, Direction.OUT); + for (IEdge edge : edges) { + if (edge.getTargetId().equals(IVertexId.from(o.getId(), o.getType())) + && edge.getType().equals(p.getName())) { + triples.add( + new Triple(triple, predicate, new Value(edge.getValue().get(predicate.getName())))); + } + } + return triples; + } + private Triple edgeToTriple(IEdge edge) { if (edge.getDirection() == Direction.OUT) { return new Triple( diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/InfGraph.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/InfGraph.java index 307662071..e67a40ec6 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/InfGraph.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/InfGraph.java @@ -16,26 +16,32 @@ import com.antgroup.openspg.reasoner.thinker.TripleStore; import com.antgroup.openspg.reasoner.thinker.logic.LogicNetwork; import com.antgroup.openspg.reasoner.thinker.logic.Result; -import com.antgroup.openspg.reasoner.thinker.logic.graph.Element; -import com.antgroup.openspg.reasoner.thinker.logic.graph.Entity; -import com.antgroup.openspg.reasoner.thinker.logic.graph.Triple; +import com.antgroup.openspg.reasoner.thinker.logic.graph.*; import com.antgroup.openspg.reasoner.thinker.logic.rule.ClauseEntry; import com.antgroup.openspg.reasoner.thinker.logic.rule.Rule; import com.antgroup.openspg.reasoner.thinker.logic.rule.TreeLogger; import com.antgroup.openspg.reasoner.thinker.logic.rule.visitor.RuleExecutor; import java.util.*; +import java.util.concurrent.LinkedBlockingDeque; import java.util.stream.Collectors; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class InfGraph implements Graph { + private static final Logger logger = LoggerFactory.getLogger(InfGraph.class); + private LogicNetwork logicNetwork; private TripleStore tripleStore; private GraphStore graphStore; + private Set recorder; public InfGraph(LogicNetwork logicNetwork, GraphStore graphStore) { this.logicNetwork = logicNetwork; this.tripleStore = new MemTripleStore(); this.graphStore = graphStore; + this.recorder = new HashSet<>(); } @Override @@ -45,17 +51,32 @@ public void init(Map param) { @Override public List find(Triple pattern, Map context) { + logger.info("InfGraph find pattern={}, context={}", pattern, context); + List result = new LinkedList<>(); prepareContext(context); // Step1: find pattern in graph List dataInGraph = graphStore.find(pattern, context); + logger.info("GraphStore find pattern={}, result={}", pattern, dataInGraph); if (CollectionUtils.isNotEmpty(dataInGraph)) { - return dataInGraph; + for (Result tri : dataInGraph) { + addTriple((Triple) tri.getData()); + result.add(tri); + } } - return inference(pattern, context); + recorder.add((Triple) pattern.cleanAlias()); + List infResult = inference(pattern, context); + logger.info("InfGraph infer pattern={}, result={}", pattern, infResult); + result.addAll(infResult); + return result; } @Override - public List find(Element s, Map context) { + public List find(Node s, Map context) { + prepareContext(context); + return inference(s, context); + } + + public List find(Entity s, Map context) { prepareContext(context); return inference(s, context); } @@ -75,44 +96,273 @@ private List inference(Element pattern, Map context) { for (Rule rule : logicNetwork.getBackwardRules(pattern)) { List body = rule.getBody().stream().map(ClauseEntry::toElement).collect(Collectors.toList()); - List data = prepareElements(body); + List> data = prepareElements(body, rule.getHead().toElement(), pattern, context); if (CollectionUtils.isEmpty(data)) { - continue; - } - TreeLogger traceLogger = new TreeLogger(rule.getRoot().toString()); - Boolean ret = - rule.getRoot() - .accept( - data.stream().map(Result::getData).collect(Collectors.toList()), - context, - new RuleExecutor(), - traceLogger); - if (ret) { - rst.add(new Result(rule.getHead().toElement(), traceLogger)); + TreeLogger traceLogger = new TreeLogger(rule.getRoot().toString()); + Boolean ret = + rule.getRoot().accept(new LinkedList<>(), context, new RuleExecutor(), traceLogger); + traceLogger.setCurrentNodeMsg(rule.getDesc()); + if (ret) { + Element ele = rule.getHead().toElement(); + rst.add(new Result(ele, traceLogger)); + if (ele instanceof Triple) { + addTriple((Triple) ele); + } else { + addEntity((Entity) ele); + } + } + } else { + for (List d : data) { + TreeLogger traceLogger = new TreeLogger(rule.getRoot().toString()); + List dList = d.stream().map(Result::getData).collect(Collectors.toList()); + Boolean ret = rule.getRoot().accept(dList, context, new RuleExecutor(), traceLogger); + List msg = + d.stream() + .map(Result::getTraceLog) + .filter(l -> l != null) + .map(TreeLogger::getCurrentNodeMsg) + .filter(m -> StringUtils.isNotBlank(m)) + .collect(Collectors.toList()); + List msgs = new LinkedList<>(msg); + if (StringUtils.isNotBlank(rule.getDesc())) { + msgs.add(rule.getDesc()); + } + traceLogger.setCurrentNodeMsg(StringUtils.join(msgs, ";")); + if (ret) { + Element ele = rule.getHead().toElement(); + rst.add(new Result(bindResult(dList, ele), traceLogger)); + if (ele instanceof Triple) { + addTriple((Triple) ele); + } else { + addEntity((Entity) ele); + } + } + } } } return rst; } - private List prepareElements(List body) { - List elements = new ArrayList<>(); - for (Element pattern : body) { - Collection spo = prepareElement(pattern); - if (spo != null && !spo.isEmpty()) { - elements.addAll(spo); + private Element bindResult(List data, Element pattern) { + Map map = new HashMap<>(); + for (Element e : data) { + if (e instanceof Entity || e instanceof Node) { + map.put(e.alias(), e); + } else if (e instanceof Triple) { + map.put(((Triple) e).getSubject().alias(), ((Triple) e).getSubject()); + map.put(((Triple) e).getObject().alias(), ((Triple) e).getObject()); + } + } + if (pattern instanceof Entity) { + return pattern; + } else if (pattern instanceof Node) { + return map.get(pattern.alias()); + } + Triple t = (Triple) pattern; + return new Triple( + map.getOrDefault(t.getSubject().alias(), ((Triple) pattern).getSubject()), + t.getPredicate(), + map.getOrDefault(t.getObject().alias(), ((Triple) pattern).getObject())); + } + + private List> prepareElements( + List body, Element head, Element pattern, Map context) { + List> elements = new ArrayList<>(); + Set choose = new HashSet<>(); + Queue starts = new LinkedBlockingDeque<>(); + Element start = getStart(pattern, head); + starts.add(start); + while (choose.size() < body.size()) { + Element s = starts.poll(); + if (s == null) { + break; + } + if (s instanceof Node && elements.isEmpty()) { + break; + } + for (Element e : body) { + if (choose.contains(e)) { + continue; + } + if (e instanceof Entity) { + choose.add(e); + if (CollectionUtils.isEmpty(elements)) { + elements.add(new LinkedList<>(prepareElement(e, context))); + } else { + for (List evidence : elements) { + evidence.addAll(prepareElement(e, context)); + } + } + } else { + Triple t = (Triple) e; + if (t.getSubject().alias().equals(s.alias())) { + starts.add(t.getObject()); + } else if (t.getObject().alias().equals(s.alias())) { + starts.add(t.getSubject()); + } else if (t.getSubject() instanceof Predicate) { + if (choose.stream() + .filter(ele -> ele instanceof Triple) + .filter(ele -> ((Triple) ele).getPredicate().alias() == t.getSubject().alias()) + .count() + == 0) { + continue; + } + } else { + continue; + } + choose.add(e); + if (CollectionUtils.isEmpty(elements)) { + Triple triple = buildTriple(null, s, t); + List> singeRst = prepareElement(null, triple, context); + if (CollectionUtils.isNotEmpty(singeRst)) { + elements.addAll(singeRst); + } + } else { + List> tmpElements = new LinkedList<>(); + for (List evidence : elements) { + Triple triple = buildTriple(evidence, s, t); + if (triple != null) { + List> singeRst = prepareElement(evidence, triple, context); + if (CollectionUtils.isNotEmpty(singeRst)) { + tmpElements.addAll(singeRst); + } + } + } + elements.clear(); + elements = tmpElements; + } + } } } return elements; } - private Collection prepareElement(Element pattern) { - Collection result; + private Triple buildTriple(List evidence, Element s, Triple triple) { + Entity entity = null; + Triple trip = null; + if (CollectionUtils.isEmpty(evidence)) { + entity = (Entity) s; + } else { + for (Result r : evidence) { + Element e = r.getData(); + if (triple.getSubject() instanceof Predicate) { + if (e instanceof Triple + && ((Triple) e).getPredicate().alias() == triple.getSubject().alias()) { + trip = (Triple) e; + } + } else { + if (e instanceof Entity && e.alias() == s.alias()) { + entity = (Entity) r.getData(); + } else if (e instanceof Triple && ((Triple) e).getSubject().alias() == s.alias()) { + entity = (Entity) ((Triple) e).getSubject(); + } else if (e instanceof Triple && ((Triple) e).getObject().alias() == s.alias()) { + entity = (Entity) ((Triple) e).getObject(); + } + } + } + } + + if (entity == null && trip == null) { + return null; + } + if (triple.getSubject().alias() == s.alias()) { + return new Triple(entity, triple.getPredicate(), triple.getObject()); + } else if (triple.getObject().alias() == s.alias()) { + return new Triple(triple.getSubject(), triple.getPredicate(), entity); + } else if (triple.getSubject() instanceof Predicate && trip != null) { + return new Triple(trip, triple.getPredicate(), triple.getObject()); + } else { + return null; + } + } + + private Element getStart(Element element, Element pattern) { + if (element instanceof Entity || element instanceof Node) { + return element.bind(pattern); + } else if (((Triple) element).getSubject() instanceof Entity) { + Element subject = ((Triple) pattern).getSubject(); + return ((Triple) element).getSubject().bind(subject); + } else { + Element object = ((Triple) pattern).getObject(); + return ((Triple) element).getObject().bind(object); + } + } + + private List> prepareElement( + List evidences, Triple pattern, Map context) { + List> rst = new LinkedList<>(); + if (CollectionUtils.isEmpty(evidences)) { + Collection curRst = prepareElement(pattern, context); + for (Result r : curRst) { + rst.add(new LinkedList<>(Arrays.asList(r))); + } + return rst; + } + Collection curRst = prepareElement(pattern, context); + for (Result r : curRst) { + List merged = new LinkedList<>(evidences); + merged.add(r); + if (reserve(merged)) { + rst.add(merged); + } + } + return rst; + } + + private Boolean reserve(List evidence) { + Map aliasToId = new HashMap<>(); + for (Result r : evidence) { + Element e = r.getData(); + if (e instanceof Entity) { + if (!aliasToId.containsKey(e.alias())) { + aliasToId.put(e.alias(), ((Entity) e).getId()); + } + if (!StringUtils.equals(((Entity) e).getId(), aliasToId.get(e.alias()))) { + return false; + } + } else if (!(((Triple) e).getSubject() instanceof Triple)) { + if (((Triple) e).getSubject() instanceof Entity) { + Entity s = (Entity) ((Triple) e).getSubject(); + if (!aliasToId.containsKey(s.alias())) { + aliasToId.put(s.alias(), s.getId()); + } + if (!StringUtils.equals(s.getId(), aliasToId.get(s.alias()))) { + return false; + } + } + if (((Triple) e).getObject() instanceof Entity) { + Entity o = (Entity) ((Triple) e).getObject(); + + if (!aliasToId.containsKey(o.alias())) { + aliasToId.put(o.alias(), o.getId()); + } + + if (!StringUtils.equals(o.getId(), aliasToId.get(o.alias()))) { + return false; + } + } + } + } + return true; + } + + private Collection prepareElement(Element pattern, Map context) { + Collection result = new LinkedList<>(); Collection spo = this.tripleStore.find(pattern); if (spo == null || spo.isEmpty()) { - result = find(pattern, null); + if (pattern instanceof Node) { + result = find((Node) pattern, context); + } else if (pattern instanceof Entity) { + result = find((Entity) pattern, context); + } else if (!recorder.contains(pattern.cleanAlias())) { + result = find((Triple) pattern, context); + } } else { result = spo.stream().map(e -> new Result(e, null)).collect(Collectors.toList()); } + for (Result r : result) { + r.setData(r.getData().bind(pattern)); + } return result; } @@ -126,5 +376,6 @@ public void addTriple(Triple triple) { public void clear() { this.tripleStore.clear(); + this.recorder.clear(); } } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/MemTripleStore.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/MemTripleStore.java index 3fb69356b..8fb2596df 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/MemTripleStore.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/MemTripleStore.java @@ -58,13 +58,22 @@ private Collection findEntity(Entity pattern) { } private Collection findTriple(Triple tripleMatch) { + Triple t = (Triple) tripleMatch.cleanAlias(); List elements = new LinkedList<>(); - if (tripleMatch.getSubject() instanceof Entity) { - elements.addAll(sToTriple.getOrDefault(tripleMatch.getSubject(), new LinkedList<>())); - } else if (tripleMatch.getObject() instanceof Entity) { - elements.addAll(oToTriple.getOrDefault(tripleMatch.getObject(), new LinkedList<>())); + if (t.getSubject() instanceof Entity || t.getSubject() instanceof Triple) { + for (Triple tri : sToTriple.getOrDefault(t.getSubject(), new LinkedList<>())) { + if (t.matches(tri)) { + elements.add(tri); + } + } + } else if (t.getObject() instanceof Entity) { + for (Triple tri : oToTriple.getOrDefault(t.getObject(), new LinkedList<>())) { + if (t.matches(tri)) { + elements.add(tri); + } + } } else { - throw new RuntimeException("Cannot support " + tripleMatch); + throw new RuntimeException("Cannot support " + t); } return elements; } @@ -76,18 +85,18 @@ private String getKey(Entity entity) { @Override public void addEntity(Entity entity) { String key = getKey(entity); - entities.put(key, entity); + entities.put(key, (Entity) entity.cleanAlias()); } @Override public void addTriple(Triple triple) { + Triple t = (Triple) triple.cleanAlias(); List sTriples = - sToTriple.computeIfAbsent(triple.getSubject(), (k) -> new LinkedList<>()); - sTriples.add(triple); - if (!(triple.getObject() instanceof Value)) { - List oTriples = - oToTriple.computeIfAbsent(triple.getSubject(), (k) -> new LinkedList<>()); - oTriples.add(triple); + sToTriple.computeIfAbsent(t.getSubject().cleanAlias(), (k) -> new LinkedList<>()); + sTriples.add(t); + if (!(t.getObject() instanceof Value)) { + List oTriples = oToTriple.computeIfAbsent(t.getObject(), (k) -> new LinkedList<>()); + oTriples.add(t); } } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/Evidence.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/Evidence.java new file mode 100644 index 000000000..3a681ce7a --- /dev/null +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/Evidence.java @@ -0,0 +1,27 @@ +/* + * 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.thinker.logic; + +import com.antgroup.openspg.reasoner.thinker.logic.graph.Element; +import java.util.List; +import lombok.Data; + +@Data +public class Evidence { + private List evidence; + + public Evidence(List evidence) { + this.evidence = evidence; + } +} diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/LogicNetwork.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/LogicNetwork.java index 1cfa3c328..6827d91fe 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/LogicNetwork.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/LogicNetwork.java @@ -19,39 +19,43 @@ import java.util.*; import java.util.stream.Collectors; import lombok.Data; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @Data public class LogicNetwork { - private Map ruleMap; - private Map> forwardRules; - private Map, Rule>> backwardRules; + private static final Logger logger = LoggerFactory.getLogger(LogicNetwork.class); + + private Map>> forwardRules; + private Map, List>> backwardRules; public LogicNetwork() { this.forwardRules = new HashMap<>(); this.backwardRules = new HashMap<>(); - this.ruleMap = new HashMap<>(); } public void addRule(Rule rule) { - if (!ruleMap.containsKey(rule.getName())) { - ruleMap.put(rule.getName(), rule); - } for (ClauseEntry body : rule.getBody()) { - Map rules = + Map> rules = forwardRules.computeIfAbsent(body.toElement(), (key) -> new HashMap<>()); - rules.put(rule.getHead().toElement(), rule); + List rList = + rules.computeIfAbsent(rule.getHead().toElement(), (k) -> new LinkedList<>()); + rList.add(rule); } - Map, Rule> rules = + Map, List> rules = backwardRules.computeIfAbsent(rule.getHead().toElement(), (key) -> new HashMap<>()); - rules.put( - rule.getBody().stream().map(ClauseEntry::toElement).collect(Collectors.toList()), rule); + List rList = + rules.computeIfAbsent( + rule.getBody().stream().map(ClauseEntry::toElement).collect(Collectors.toList()), + (k) -> new LinkedList<>()); + rList.add(rule); } public Collection getForwardRules(Element e) { Set rules = new HashSet<>(); - for (Map.Entry> entry : forwardRules.entrySet()) { + for (Map.Entry>> entry : forwardRules.entrySet()) { if (entry.getKey().matches(e)) { - rules.addAll(entry.getValue().values()); + entry.getValue().values().stream().forEach(list -> rules.addAll(list)); } } return rules; @@ -59,11 +63,12 @@ public Collection getForwardRules(Element e) { public Collection getBackwardRules(Element triple) { Set rules = new HashSet<>(); - for (Map.Entry, Rule>> entry : backwardRules.entrySet()) { - if (triple.matches(entry.getKey())) { - rules.addAll(entry.getValue().values()); + for (Map.Entry, List>> entry : backwardRules.entrySet()) { + if (entry.getKey().matches(triple)) { + entry.getValue().values().stream().forEach(list -> rules.addAll(list)); } } + logger.info("LogicNetwork getBackwardRules, pattern={}, rules={}", triple, rules.size()); return rules; } } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Element.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Element.java index febc71386..d5cd0024d 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Element.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Element.java @@ -13,6 +13,7 @@ package com.antgroup.openspg.reasoner.thinker.logic.graph; +import com.antgroup.openspg.reasoner.common.exception.UnsupportedOperationException; import java.io.Serializable; public abstract class Element implements Serializable { @@ -23,4 +24,21 @@ public abstract class Element implements Serializable { public boolean matches(Element other) { return equals(other); } + + public Element bind(Element pattern) { + return this; + } + + public String alias() { + throw new UnsupportedOperationException( + this.getClass().getSimpleName() + " cannot support", null); + } + + public Element cleanAlias() { + return this; + } + + public String shortString() { + return toString(); + } } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Entity.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Entity.java index 2eeac4ca5..d17461f1c 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Entity.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Entity.java @@ -102,13 +102,46 @@ public boolean equals(Object o) { && Objects.equals(alias, entity.alias); } + public boolean matches(Element other) { + if (other == null) { + return false; + } + if (other instanceof Node) { + return Objects.equals(type, ((Node) other).getType()); + } + if (other instanceof Entity) { + return Objects.equals(type, ((Entity) other).getType()) + && Objects.equals(id, ((Entity) other).getId()); + } + return equals(other); + } + + @Override + public String alias() { + return this.alias; + } + + @Override + public Element cleanAlias() { + return new Entity(this.id, this.type); + } + @Override public int hashCode() { return Objects.hash(id, type, alias); } @Override - public String toString() { + public Element bind(Element pattern) { + if (pattern instanceof Entity || pattern instanceof Node) { + return new Entity(this.id, this.type, pattern.alias()); + } else { + return this; + } + } + + @Override + public String shortString() { StringBuilder sb = new StringBuilder(); sb.append(type).append("/").append(id); return sb.toString(); diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Node.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Node.java index a63a7134a..9807fcfe3 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Node.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Node.java @@ -52,12 +52,37 @@ public boolean matches(Element other) { if (other == null) { return false; } + if (other instanceof Any) { + return true; + } + if (other instanceof Node) { + return Objects.equals(type, ((Node) other).getType()); + } if (other instanceof Entity) { return Objects.equals(type, ((Entity) other).getType()); } return equals(other); } + @Override + public Element bind(Element pattern) { + if (pattern instanceof Entity) { + return new Entity(((Entity) pattern).getId(), this.type, ((Entity) pattern).getAlias()); + } else { + return this; + } + } + + @Override + public String alias() { + return this.alias; + } + + @Override + public Element cleanAlias() { + return new Node(this.type); + } + /** * Getter method for property type. * diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Predicate.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Predicate.java index 2477726a1..c4edc5373 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Predicate.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Predicate.java @@ -13,6 +13,7 @@ package com.antgroup.openspg.reasoner.thinker.logic.graph; +import com.antgroup.openspg.reasoner.common.exception.UnsupportedOperationException; import java.util.Objects; import lombok.Data; @@ -84,4 +85,32 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(name, alias); } + + public boolean matches(Element other) { + if (other == null) { + return false; + } + if (other instanceof Predicate) { + return Objects.equals(name, ((Predicate) other).getName()); + } + return equals(other); + } + + public String alias() { + return alias; + } + + @Override + public Element cleanAlias() { + return new Predicate(this.name); + } + + @Override + public Element bind(Element pattern) { + if (pattern instanceof Predicate) { + return new Predicate(name, pattern.alias()); + } else { + throw new UnsupportedOperationException("Triple cannot bind " + pattern.toString(), null); + } + } } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Triple.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Triple.java index 1aa393131..61b0e62b5 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Triple.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Triple.java @@ -12,6 +12,7 @@ */ package com.antgroup.openspg.reasoner.thinker.logic.graph; +import com.antgroup.openspg.reasoner.common.exception.UnsupportedOperationException; import java.util.Objects; import lombok.Data; @@ -40,6 +41,27 @@ public boolean matches(Element other) { } } + @Override + public Element bind(Element pattern) { + if (pattern instanceof Triple) { + return new Triple( + subject.bind(((Triple) pattern).getSubject()), + predicate.bind(((Triple) pattern).getPredicate()), + object.bind(((Triple) pattern).getObject())); + } else { + throw new UnsupportedOperationException("Triple cannot bind " + pattern.toString(), null); + } + } + + public String alias() { + return predicate.alias(); + } + + @Override + public Element cleanAlias() { + return new Triple(subject.cleanAlias(), predicate.cleanAlias(), object.cleanAlias()); + } + public static Triple create(Element s, Element p, Element o) { return new Triple(nullToAny(s), nullToAny(p), nullToAny(o)); } @@ -120,4 +142,15 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(subject, predicate, object); } + + @Override + public String shortString() { + StringBuilder sb = new StringBuilder(); + sb.append(subject.alias()) + .append("_") + .append(predicate.alias()) + .append("_") + .append(object.alias()); + return sb.toString(); + } } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Value.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Value.java index 424c6148c..46f145b85 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Value.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Value.java @@ -17,13 +17,79 @@ @Data public class Value extends Element { - private String name; private Object val; + private String alias; public Value() {} - public Value(String name, Object val) { - this.name = name; + public Value(Object val) { + this.val = val; + } + + public Value(Object val, String alias) { + this.val = val; + this.alias = alias; + } + + @Override + public String alias() { + return this.alias; + } + + @Override + public Element cleanAlias() { + return new Value(val); + } + + public boolean matches(Element other) { + if (other != null && other instanceof Value) { + return true; + } + return false; + } + + @Override + public Element bind(Element pattern) { + if (pattern instanceof Value) { + return new Value(val, pattern.alias()); + } else { + return this; + } + } + + /** + * Getter method for property alias. + * + * @return property value of alias + */ + public String getAlias() { + return alias; + } + + /** + * Setter method for property alias. + * + * @param alias value to be assigned to property alias + */ + public void setAlias(String alias) { + this.alias = alias; + } + + /** + * Getter method for property val. + * + * @return property value of val + */ + public Object getVal() { + return val; + } + + /** + * Setter method for property val. + * + * @param val value to be assigned to property val + */ + public void setVal(Object val) { this.val = val; } } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/TreeLogger.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/TreeLogger.java index 77c2fd966..c6322b02a 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/TreeLogger.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/TreeLogger.java @@ -31,7 +31,7 @@ public class TreeLogger implements Serializable { private static final String V_IDENT = "\u2502 "; private String currentNodeName; - private StringBuilder currentNodeMsg; + private String currentNodeMsg; private Boolean currentNodeRst; private List children; @@ -40,13 +40,8 @@ public TreeLogger(String currentNodeName) { } public TreeLogger log(Object msg) { - if (this.currentNodeMsg == null) { - this.currentNodeMsg = new StringBuilder(); - } if (msg != null) { - this.currentNodeMsg.append(msg); - } else { - this.currentNodeMsg.append("null"); + this.currentNodeMsg = msg.toString(); } return this; } @@ -133,7 +128,7 @@ public String getCurrentNodeName() { * * @return property value of currentNodeMsg */ - public StringBuilder getCurrentNodeMsg() { + public String getCurrentNodeMsg() { return currentNodeMsg; } @@ -142,7 +137,7 @@ public StringBuilder getCurrentNodeMsg() { * * @param currentNodeMsg value to be assigned to property currentNodeMsg */ - public void setCurrentNodeMsg(StringBuilder currentNodeMsg) { + public void setCurrentNodeMsg(String currentNodeMsg) { this.currentNodeMsg = currentNodeMsg; } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/exact/QlExpressCondition.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/exact/QlExpressCondition.java index 4ffdab79f..7590b1a12 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/exact/QlExpressCondition.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/exact/QlExpressCondition.java @@ -13,29 +13,104 @@ package com.antgroup.openspg.reasoner.thinker.logic.rule.exact; -import com.antgroup.openspg.reasoner.thinker.logic.graph.Element; +import com.antgroup.openspg.reasoner.thinker.logic.graph.*; import com.antgroup.openspg.reasoner.thinker.logic.rule.TreeLogger; -import com.antgroup.openspg.reasoner.udf.rule.RuleRunner; +import com.antgroup.openspg.reasoner.thinker.qlexpress.QlExpressRunner; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; import java.util.*; +import java.util.concurrent.TimeUnit; import lombok.Data; +import org.apache.commons.collections4.CollectionUtils; @Data public class QlExpressCondition extends Condition { + private static LoadingCache>> varsCache; private String qlExpress; public QlExpressCondition(String qlExpress) { this.qlExpress = qlExpress; } + static { + synchronized (QlExpressCondition.class) { + if (null == varsCache) { + LoadingCache>> tmpCache = + CacheBuilder.newBuilder() + .concurrencyLevel(8) + .maximumSize(100) + .expireAfterAccess(30, TimeUnit.MINUTES) + .build( + new CacheLoader>>() { + @Override + public Map> load(String rule) throws Exception { + try { + return ((QlExpressRunner) QlExpressRunner.getInstance()) + .getParamNames(rule); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + }); + varsCache = tmpCache; + } + } + } + @Override public Boolean execute(List spoList, Map context, TreeLogger logger) { Map ruleCtx = new HashMap<>(); - ruleCtx.putAll(context); + if (context != null) { + ruleCtx.putAll(context); + } for (Element element : spoList) { - ruleCtx.put(element.toString(), true); + ruleCtx.put(element.shortString(), true); + if (element instanceof Triple) { + Triple triple = (Triple) element; + if (triple.getObject() instanceof Value) { + Map props = + (Map) + ruleCtx.computeIfAbsent( + triple.getSubject().alias(), (k) -> new HashMap()); + props.put( + ((Predicate) ((Triple) element).getPredicate()).getName(), + ((Value) triple.getObject()).getVal()); + } + } + } + try { + boolean absent = absent(ruleCtx); + if (absent) { + return null; + } + Object rst = + QlExpressRunner.getInstance().executeExpression(ruleCtx, Arrays.asList(qlExpress), ""); + if (rst == null) { + return false; + } else { + return (Boolean) rst; + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private boolean absent(Map context) throws Exception { + if (qlExpress.toLowerCase().contains("get_value") + || qlExpress.toLowerCase().contains("get_spo")) { + return false; + } + Map> vars = varsCache.get(qlExpress); + for (String key : vars.keySet()) { + if (CollectionUtils.isNotEmpty(vars.get(key))) { + continue; + } + if (!context.containsKey(key)) { + return true; + } } - Object rst = RuleRunner.getInstance().executeExpression(ruleCtx, Arrays.asList(qlExpress), ""); - return (Boolean) rst; + return false; } @Override diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/visitor/RuleExecutor.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/visitor/RuleExecutor.java index 409c50a7f..f30d2f38f 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/visitor/RuleExecutor.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/visitor/RuleExecutor.java @@ -31,7 +31,7 @@ public Boolean visit( Boolean ret = null; for (Node child : node.getChildren()) { Boolean c = child.accept(spoList, context, this, logger.addChild(child.toString())); - c = c == null ? false : c; + c = c == null ? true : c; if (ret == null) { ret = c; } else { @@ -49,7 +49,7 @@ public Boolean visit( Boolean ret = null; for (Node child : node.getChildren()) { Boolean c = child.accept(spoList, context, this, logger.addChild(child.toString())); - c = c == null ? false : c; + c = c == null ? true : c; if (ret == null) { ret = c; } else { @@ -67,7 +67,7 @@ public Boolean visit( Boolean ret = null; Node child = node.getChild(); Boolean r = child.accept(spoList, context, this, logger); - r = r == null ? false : r; + r = r == null ? true : r; ret = !r; logger.log(ret); logger.setCurrentNodeRst(ret); @@ -78,9 +78,8 @@ public Boolean visit( public Boolean visit( Condition node, List spoList, Map context, TreeLogger logger) { Boolean ret = node.execute(spoList, context, logger); - ret = ret == null ? false : ret; logger.log(ret); logger.setCurrentNodeRst(ret); - return ret; + return ret == null ? true : ret; } } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/qlexpress/QlExpressRunner.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/qlexpress/QlExpressRunner.java new file mode 100644 index 000000000..99faf5078 --- /dev/null +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/qlexpress/QlExpressRunner.java @@ -0,0 +1,121 @@ +/* + * 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.thinker.qlexpress; + +import com.antgroup.openspg.reasoner.thinker.qlexpress.op.RichOperatorEqualsLessMore; +import com.antgroup.openspg.reasoner.udf.rule.RuleRunner; +import com.google.common.collect.Lists; +import com.ql.util.express.InstructionSet; +import com.ql.util.express.Operator; +import com.ql.util.express.instruction.detail.Instruction; +import com.ql.util.express.instruction.detail.InstructionConstData; +import com.ql.util.express.instruction.detail.InstructionLoadAttr; +import com.ql.util.express.instruction.detail.InstructionOperator; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import scala.Tuple2; + +public class QlExpressRunner extends RuleRunner { + private static volatile QlExpressRunner instance = null; + + private QlExpressRunner() {} + + public static QlExpressRunner getInstance() { + if (null != instance) { + return instance; + } + synchronized (QlExpressRunner.class) { + if (instance == null) { + QlExpressRunner runner = new QlExpressRunner(); + runner.init(); + instance = runner; + } + } + return instance; + } + + @Override + protected void init() { + super.init(); + overrideOperator(); + } + + public Map> getParamNames(String rule) throws Exception { + InstructionSet instructionSet = EXPRESS_RUNNER.getInstructionSetFromLocalCache(rule); + Map> result = new TreeMap<>(); + for (int i = 0; i < instructionSet.getInstructionLength(); i++) { + Instruction instruction = instructionSet.getInstruction(i); + if (instruction instanceof InstructionLoadAttr) { + if ("null".equals(((InstructionLoadAttr) instruction).getAttrName())) { + continue; + } + String aliasName = ((InstructionLoadAttr) instruction).getAttrName(); + Set varSet = result.computeIfAbsent(aliasName, (k) -> new HashSet<>()); + // LoadAttr之后就是具体的属性名 + Instruction next = instructionSet.getInstruction(i + 1); + if (next instanceof InstructionOperator) { + String opName = ((InstructionOperator) next).getOperator().getName(); + if ("FieldCall".equalsIgnoreCase(opName)) { + String varName = ((InstructionOperator) next).getOperator().toString().split(":")[1]; + varSet.add(varName); + } + } + } + } + + for (int i = 0; i < instructionSet.getInstructionLength(); i++) { + Instruction instruction = instructionSet.getInstruction(i); + if (instruction instanceof InstructionOperator) { + String opName = ((InstructionOperator) instruction).getOperator().getName(); + if (opName != null) { + if (opName.equalsIgnoreCase("def") || opName.equalsIgnoreCase("exportDef")) { + if (i >= 1) { + String varLocalName = + (String) + ((InstructionConstData) instructionSet.getInstruction(i - 1)) + .getOperateData() + .getObject(null); + result.remove(varLocalName); + } + } else if (opName.equalsIgnoreCase("alias") || opName.equalsIgnoreCase("exportAlias")) { + if (i >= 2) { + String varLocalName = + (String) + ((InstructionConstData) instructionSet.getInstruction(i - 2)) + .getOperateData() + .getObject(null); + result.remove(varLocalName); + } + } + } + } + } + return result; + } + + @Override + protected void overrideOperator() { + Lists.newArrayList( + new Tuple2("<", new RichOperatorEqualsLessMore("<")), + new Tuple2(">", new RichOperatorEqualsLessMore(">")), + new Tuple2("<=", new RichOperatorEqualsLessMore("<=")), + new Tuple2(">=", new RichOperatorEqualsLessMore(">=")), + new Tuple2("==", new RichOperatorEqualsLessMore("==")), + new Tuple2("!=", new RichOperatorEqualsLessMore("!=")), + new Tuple2("<>", new RichOperatorEqualsLessMore("<>"))) + .forEach(udfTuple -> EXPRESS_RUNNER.replaceOperator(udfTuple._1(), udfTuple._2())); + } +} diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/qlexpress/op/RichOperatorEqualsLessMore.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/qlexpress/op/RichOperatorEqualsLessMore.java new file mode 100644 index 000000000..2623ea532 --- /dev/null +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/qlexpress/op/RichOperatorEqualsLessMore.java @@ -0,0 +1,144 @@ +/* + * 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.thinker.qlexpress.op; + +import com.antgroup.openspg.reasoner.udf.rule.op.OperatorEqualsLessMore; +import java.math.BigDecimal; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.apache.commons.lang3.math.NumberUtils; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; + +/** + * Custom comparison operator logic: + * + *

1. When comparing strings with numerical types, convert the string to a number. 2. Support + * evaluation of expressions of the type ">150" > 150. + */ +public class RichOperatorEqualsLessMore extends OperatorEqualsLessMore { + private static final String reg = "[<>]?\\s*\\d+(\\.\\d+)?"; + private Pattern pattern; + + public RichOperatorEqualsLessMore(String aName) { + super(aName); + pattern = Pattern.compile(reg); + } + + public RichOperatorEqualsLessMore(String aAliasName, String aName, String aErrorInfo) { + super(aAliasName, aName, aErrorInfo); + pattern = Pattern.compile(reg); + } + + @Override + public Object executeInner(Object[] list) throws Exception { + return executeInner(list[0], list[1]); + } + + @Override + public Object executeInner(Object op1, Object op2) { + if (op1 instanceof Number && op2 instanceof String) { + String value = getValue((String) op2); + if (value == null) { + return null; + } else if (NumberUtils.isCreatable(value)) { + return executeInner(this.name, op1, toNumber((String) op2, op1.getClass())); + } else { + Number num = toNumber(value.substring(1, value.length()), op1.getClass()); + Pair right = buildRange(value.substring(0, 1), num); + Pair left = buildRange(this.name, (Number) op1); + if (left == null || right == null) { + return OperatorEqualsLessMore.executeInner(this.name, op1, op2); + } else { + return executeInner(left, right); + } + } + } else if (op1 instanceof String && op2 instanceof Number) { + String value = getValue((String) op1); + if (value == null) { + return null; + } else if (NumberUtils.isCreatable(value)) { + return executeInner(this.name, toNumber((String) op1, op2.getClass()), op2); + } else { + Number num = toNumber(value.substring(1, value.length()), op2.getClass()); + Pair right = buildRange(value.substring(0, 1), num); + Pair left = buildRange(this.name, (Number) op2); + if (left == null || right == null) { + return OperatorEqualsLessMore.executeInner(this.name, op1, op2); + } else { + return executeInner(left, right); + } + } + } else { + return OperatorEqualsLessMore.executeInner(this.name, op1, op2); + } + } + + private Pair buildRange(String opName, Number op) { + if (">=".equals(opName) || ">".equals(opName)) { + return new ImmutablePair<>(op, Long.MAX_VALUE); + } else if ("<=".equals(opName) || "<".equals(opName)) { + return new ImmutablePair<>(Long.MIN_VALUE, op); + } else { + return null; + } + } + + private Boolean executeInner(Pair left, Pair right) { + if (OperatorEqualsLessMore.executeInner(">", left.getLeft(), right.getRight()) + || OperatorEqualsLessMore.executeInner("<", left.getRight(), right.getLeft())) { + return false; + } else if (OperatorEqualsLessMore.executeInner("<=", left.getLeft(), right.getLeft()) + && OperatorEqualsLessMore.executeInner(">=", left.getRight(), right.getRight())) { + return true; + } else { + return null; + } + } + + private String getValue(String content) { + Matcher matcher = pattern.matcher(content); + if (matcher.find()) { + return matcher.group(); + } else { + return null; + } + } + + private Number toNumber(String op, Class numberClass) { + int type = getSeq(numberClass); + if (NUMBER_TYPE_BYTE == type) { + return Byte.valueOf(op); + } + if (NUMBER_TYPE_SHORT == type) { + return Short.valueOf(op); + } + if (NUMBER_TYPE_INT == type) { + return Integer.valueOf(op); + } + if (NUMBER_TYPE_LONG == type) { + return Long.valueOf(op); + } + if (NUMBER_TYPE_FLOAT == type) { + return Float.valueOf(op); + } + if (NUMBER_TYPE_DOUBLE == type) { + return Double.valueOf(op); + } + if (NUMBER_TYPE_DECIMAL == type) { + return new BigDecimal(op); + } + return null; + } +} diff --git a/reasoner/thinker/src/main/scala/com/antgroup/openspg/reasoner/thinker/ThinkerRuleParser.scala b/reasoner/thinker/src/main/scala/com/antgroup/openspg/reasoner/thinker/ThinkerRuleParser.scala index 146cdffa2..f3a28e0e5 100644 --- a/reasoner/thinker/src/main/scala/com/antgroup/openspg/reasoner/thinker/ThinkerRuleParser.scala +++ b/reasoner/thinker/src/main/scala/com/antgroup/openspg/reasoner/thinker/ThinkerRuleParser.scala @@ -30,7 +30,7 @@ import com.antgroup.openspg.reasoner.lube.common.expr.{ } import com.antgroup.openspg.reasoner.lube.utils.transformer.impl.Expr2QlexpressTransformer import com.antgroup.openspg.reasoner.parser.expr.RuleExprParser -import com.antgroup.openspg.reasoner.thinker.logic.graph.{Element, Entity, Predicate} +import com.antgroup.openspg.reasoner.thinker.logic.graph.{Element, Entity, Predicate, Value} import com.antgroup.openspg.reasoner.thinker.logic.graph import com.antgroup.openspg.reasoner.thinker.logic.rule.{ ClauseEntry, @@ -51,6 +51,10 @@ class ThinkerRuleParser extends RuleExprParser { val expr2StringTransformer = new Expr2QlexpressTransformer() var defaultAliasNum = 0 val aliasToElementMap = new mutable.HashMap[String, Element]() + val spoRuleToSpoSetMap = new mutable.HashMap[String, (Element, Element, Element)]() + + val valueTypeSet: Set[String] = + Set.apply("STRING", "LONG", "INT", "INTEGER", "DOUBLE", "FLOAT", "BOOLEAN") val conditionToElementMap: mutable.HashMap[Condition, mutable.HashSet[ClauseEntry]] = new mutable.HashMap() @@ -129,6 +133,7 @@ class ThinkerRuleParser extends RuleExprParser { case e: Entity => e.getAlias case p: Predicate => p.getAlias case n: logic.graph.Node => n.getAlias + case v: Value => v.getAlias case _ => throw new IllegalArgumentException("%s element has no alias".format(element)) } } @@ -273,7 +278,7 @@ class ThinkerRuleParser extends RuleExprParser { val propertyName: String = propertyNameList.head.getText val subject = aliasToElementMap(alias) val predicate = new Predicate(propertyName) - val o = new graph.Any() + val o = new Value(null, "anonymous_" + getDefaultAliasNum) body += new TriplePattern(new logic.graph.Triple(subject, predicate, o)) } } @@ -314,10 +319,16 @@ class ThinkerRuleParser extends RuleExprParser { } def parseSpoRule(ctx: Spo_ruleContext, isHead: Boolean = false): (Element, Element, Element) = { + val spoRuleText = ctx.getText + if (spoRuleToSpoSetMap.contains(spoRuleText)) { + return spoRuleToSpoSetMap(spoRuleText) + } val sNode: Element = parseNode(ctx, 0, isHead) val pPredicate: Element = parsePredicate(ctx) val oNode: Element = parseNode(ctx, 1) - (sNode, pPredicate, oNode) + val tmpResult = (sNode, pPredicate, oNode) + spoRuleToSpoSetMap += (spoRuleText -> tmpResult) + tmpResult } def parseNode(ctx: Spo_ruleContext, index: Int, isHead: Boolean = false): Element = { @@ -339,6 +350,8 @@ class ThinkerRuleParser extends RuleExprParser { val conceptEntity = constructConceptEntity(conceptContext) conceptEntity.setAlias(sAlias) sNode = conceptEntity + } else if (valueTypeSet.contains(sType.toUpperCase())) { + sNode = new Value(null, sAlias) } } aliasToElementMap += (sAlias -> sNode) diff --git a/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/DefaultThinkerTests.java b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/DefaultThinkerTests.java index 45221ede2..d2e910316 100644 --- a/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/DefaultThinkerTests.java +++ b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/DefaultThinkerTests.java @@ -13,20 +13,17 @@ package com.antgroup.openspg.reasoner.thinker; -import com.antgroup.openspg.reasoner.common.constants.Constants; -import com.antgroup.openspg.reasoner.common.graph.edge.Direction; import com.antgroup.openspg.reasoner.common.graph.edge.impl.Edge; import com.antgroup.openspg.reasoner.common.graph.property.IProperty; -import com.antgroup.openspg.reasoner.common.graph.property.impl.VertexVersionProperty; import com.antgroup.openspg.reasoner.common.graph.vertex.IVertexId; import com.antgroup.openspg.reasoner.common.graph.vertex.impl.Vertex; import com.antgroup.openspg.reasoner.graphstate.GraphState; -import com.antgroup.openspg.reasoner.graphstate.impl.MemGraphState; import com.antgroup.openspg.reasoner.thinker.catalog.MockLogicCatalog; import com.antgroup.openspg.reasoner.thinker.engine.DefaultThinker; import com.antgroup.openspg.reasoner.thinker.logic.Result; import com.antgroup.openspg.reasoner.thinker.logic.graph.Entity; import com.antgroup.openspg.reasoner.thinker.logic.graph.Node; +import com.antgroup.openspg.reasoner.thinker.logic.graph.Predicate; import com.antgroup.openspg.reasoner.thinker.logic.rule.Rule; import java.util.*; import org.junit.Assert; @@ -34,79 +31,16 @@ public class DefaultThinkerTests { private GraphState buildGraphState() { - GraphState graphState = new MemGraphState(); + Vertex a1 = GraphUtil.makeVertex("a1", "A"); + Vertex a2 = GraphUtil.makeVertex("a2", "A"); + Vertex b = GraphUtil.makeVertex("b", "B"); + Vertex c = GraphUtil.makeVertex("c", "C"); - Vertex vertex1 = - new Vertex<>(IVertexId.from("a1", "A"), new VertexVersionProperty()); - Vertex vertex2 = - new Vertex<>(IVertexId.from("a2", "A"), new VertexVersionProperty()); - Vertex vertex3 = - new Vertex<>(IVertexId.from("b", "B"), new VertexVersionProperty()); - Vertex vertex4 = - new Vertex<>(IVertexId.from("c", "C"), new VertexVersionProperty("k1", "abcd")); + Edge a1b = GraphUtil.makeEdge(a1, b, "ab"); + Edge a2b = GraphUtil.makeEdge(a2, b, "ab"); + Edge bc = GraphUtil.makeEdge(b, c, "bc"); - Edge a1b = - new Edge( - IVertexId.from("a1", "A"), - IVertexId.from("b", "B"), - new VertexVersionProperty( - Constants.EDGE_FROM_ID_TYPE_KEY, - "A", - Constants.EDGE_TO_ID_TYPE_KEY, - "B", - Constants.EDGE_FROM_ID_KEY, - "a1", - Constants.EDGE_TO_ID_KEY, - "b"), - 0L, - Direction.OUT, - "ab"); - Edge a2b = - new Edge( - IVertexId.from("a2", "A"), - IVertexId.from("b", "B"), - new VertexVersionProperty( - Constants.EDGE_FROM_ID_TYPE_KEY, - "A", - Constants.EDGE_TO_ID_TYPE_KEY, - "B", - Constants.EDGE_FROM_ID_KEY, - "a1", - Constants.EDGE_TO_ID_KEY, - "b"), - 0L, - Direction.OUT, - "ab"); - Edge bc = - new Edge( - IVertexId.from("b", "B"), - IVertexId.from("c", "C"), - new VertexVersionProperty( - Constants.EDGE_FROM_ID_TYPE_KEY, - "B", - Constants.EDGE_TO_ID_TYPE_KEY, - "C", - Constants.EDGE_FROM_ID_KEY, - "b", - Constants.EDGE_TO_ID_KEY, - "c"), - 0L, - Direction.OUT, - "bc"); - - graphState.addVertex(vertex1); - graphState.addEdges(vertex1.getId(), new LinkedList<>(), Arrays.asList(a1b)); - graphState.addVertex(vertex2); - graphState.addEdges(vertex2.getId(), new LinkedList<>(), Arrays.asList(a2b)); - - graphState.addVertex(vertex3); - graphState.addEdges( - vertex3.getId(), Arrays.asList(a1b.reverse(), a2b.reverse()), Arrays.asList(bc)); - - graphState.addVertex(vertex4); - graphState.addEdges(vertex4.getId(), Arrays.asList(bc.reverse()), new LinkedList<>()); - - return graphState; + return GraphUtil.buildMemState(Arrays.asList(a1, a2, b, c), Arrays.asList(a1b, a2b, bc)); } @Test @@ -115,7 +49,7 @@ public void testFindForward() { mockLogicCatalog.init(); Thinker thinker = new DefaultThinker(buildGraphState(), mockLogicCatalog); List triples = thinker.find(new Entity("a1", "A"), null, null); - Assert.assertTrue(triples.size() == 1); + Assert.assertTrue(triples.size() == 2); } @Test @@ -133,8 +67,18 @@ private Rule getR1() { return parser.parseSimplifyDsl(rule, null).head(); } + private Rule getR2() { + String rule = + "Define (a:A)-[:ac]->(c:C) {\n" + + " R1: (a)-[: ab]->(b: B) AND (b)-[:bc]->(c:C) \n" + + "}\n" + + "Description: \"(a, ab, b), (b, bc, c) -> (a, ac, c)\""; + SimplifyThinkerParser parser = new SimplifyThinkerParser(); + return parser.parseSimplifyDsl(rule, null).head(); + } + @Test - public void testFindWithRule() { + public void testFindWithRule1() { MockLogicCatalog logicCatalog = new MockLogicCatalog(Arrays.asList(getR1())); logicCatalog.init(); Thinker thinker = new DefaultThinker(buildGraphState(), logicCatalog); @@ -146,4 +90,15 @@ public void testFindWithRule() { List triples = thinker.find(new Node("D"), context); Assert.assertTrue(triples.size() == 1); } + + @Test + public void testFindWithRule2() { + MockLogicCatalog logicCatalog = new MockLogicCatalog(Arrays.asList(getR2())); + logicCatalog.init(); + Thinker thinker = new DefaultThinker(buildGraphState(), logicCatalog); + List triples = thinker.find(new Entity("a1", "A"), new Predicate("ac"), new Node("C")); + Assert.assertTrue(triples.size() == 1); + triples = thinker.find(new Node("A"), new Predicate("ac"), new Entity("c", "C")); + Assert.assertTrue(triples.size() == 2); + } } diff --git a/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/GraphUtil.java b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/GraphUtil.java new file mode 100644 index 000000000..b82eeaa42 --- /dev/null +++ b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/GraphUtil.java @@ -0,0 +1,81 @@ +/* + * 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.thinker; + +import com.antgroup.openspg.reasoner.common.Utils; +import com.antgroup.openspg.reasoner.common.constants.Constants; +import com.antgroup.openspg.reasoner.common.graph.edge.Direction; +import com.antgroup.openspg.reasoner.common.graph.edge.IEdge; +import com.antgroup.openspg.reasoner.common.graph.edge.impl.Edge; +import com.antgroup.openspg.reasoner.common.graph.property.IProperty; +import com.antgroup.openspg.reasoner.common.graph.property.IVersionProperty; +import com.antgroup.openspg.reasoner.common.graph.property.impl.VertexVersionProperty; +import com.antgroup.openspg.reasoner.common.graph.vertex.IVertex; +import com.antgroup.openspg.reasoner.common.graph.vertex.IVertexId; +import com.antgroup.openspg.reasoner.common.graph.vertex.impl.Vertex; +import com.antgroup.openspg.reasoner.graphstate.impl.MemGraphState; +import java.util.*; + +public class GraphUtil { + public static Edge makeEdge( + Vertex from, + Vertex to, + String edgeType, + Object... kvs) { + Map> props = new HashMap<>(); + Map propertyMap = Utils.convert2Property(kvs); + propertyMap.put(Constants.EDGE_FROM_ID_TYPE_KEY, from.getId().getType()); + propertyMap.put(Constants.EDGE_TO_ID_TYPE_KEY, to.getId().getType()); + propertyMap.put(Constants.EDGE_FROM_ID_KEY, from.getValue().get(Constants.NODE_ID_KEY)); + propertyMap.put(Constants.EDGE_TO_ID_KEY, to.getValue().get(Constants.NODE_ID_KEY)); + for (Map.Entry entry : propertyMap.entrySet()) { + TreeMap valueMap = new TreeMap<>(); + valueMap.put(0L, entry.getValue()); + props.put(entry.getKey(), valueMap); + } + return new Edge<>( + from.getId(), to.getId(), new VertexVersionProperty(props), 0L, Direction.OUT, edgeType); + } + + public static Vertex makeVertex(String id, String type, Object... kvs) { + Map> props = new HashMap<>(); + Map propertyMap = Utils.convert2Property(kvs); + propertyMap.put(Constants.NODE_ID_KEY, id); + for (Map.Entry entry : propertyMap.entrySet()) { + TreeMap valueMap = new TreeMap<>(); + valueMap.put(0L, entry.getValue()); + props.put(entry.getKey(), valueMap); + } + return new Vertex<>(IVertexId.from(id, type), new VertexVersionProperty(props)); + } + + public static MemGraphState buildMemState( + List> vertexList, List> edgeList) { + MemGraphState graphState = new MemGraphState(); + for (IVertex v : vertexList) { + graphState.addVertex(v); + List> outEdges = new LinkedList<>(); + List> inEdges = new LinkedList<>(); + for (IEdge e : edgeList) { + if (v.getId().equals(e.getSourceId())) { + outEdges.add(e); + } else if (v.getId().equals(e.getTargetId())) { + inEdges.add(e.reverse()); + } + } + graphState.addEdges(v.getId(), inEdges, outEdges); + } + return graphState; + } +} diff --git a/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/HypertensionTest.java b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/HypertensionTest.java new file mode 100644 index 000000000..0f82cd851 --- /dev/null +++ b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/HypertensionTest.java @@ -0,0 +1,73 @@ +/* + * 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.thinker; + +import com.antgroup.openspg.reasoner.common.graph.vertex.IVertexId; +import com.antgroup.openspg.reasoner.graphstate.GraphState; +import com.antgroup.openspg.reasoner.graphstate.impl.MemGraphState; +import com.antgroup.openspg.reasoner.thinker.catalog.ResourceLogicCatalog; +import com.antgroup.openspg.reasoner.thinker.engine.DefaultThinker; +import com.antgroup.openspg.reasoner.thinker.logic.Result; +import com.antgroup.openspg.reasoner.thinker.logic.graph.Node; +import com.antgroup.openspg.reasoner.thinker.logic.graph.Predicate; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.Assert; +import org.junit.Test; + +public class HypertensionTest { + private GraphState buildGraphState() { + GraphState graphState = new MemGraphState(); + return graphState; + } + + private Thinker buildThinker() { + ResourceLogicCatalog logicCatalog = new ResourceLogicCatalog("/Hypertension.txt"); + logicCatalog.init(); + Thinker thinker = new DefaultThinker(buildGraphState(), logicCatalog); + return thinker; + } + + @Test + public void bloodPressureLevel() { + Thinker thinker = buildThinker(); + Map context = new HashMap<>(); + context.put("收缩压", 160); + List triples = thinker.find(new Node("血压水平分级"), context); + Assert.assertTrue(triples.size() == 3); + } + + @Test + public void hypertensionLevel() { + Thinker thinker = buildThinker(); + Map context = new HashMap<>(); + context.put("BMI", 35); + context.put("GFR", 35); + List triples = thinker.find(new Node("高血压分层"), context); + Assert.assertTrue(triples.size() == 2); + } + + @Test + public void combinationDrug() { + Thinker thinker = buildThinker(); + Map context = new HashMap<>(); + context.put("收缩压", 160); + context.put("目标收缩压上界", 140); + context.put("BMI", 35); + context.put("GFR", 35); + List triples = thinker.find(null, new Predicate("基本用药方案"), new Node("药品"), context); + Assert.assertTrue(triples.size() == 2); + } +} diff --git a/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/InsuranceTests.java b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/InsuranceTests.java new file mode 100644 index 000000000..d30a98436 --- /dev/null +++ b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/InsuranceTests.java @@ -0,0 +1,124 @@ +/* + * 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.thinker; + +import com.antgroup.openspg.reasoner.common.graph.edge.IEdge; +import com.antgroup.openspg.reasoner.common.graph.edge.impl.Edge; +import com.antgroup.openspg.reasoner.common.graph.property.IProperty; +import com.antgroup.openspg.reasoner.common.graph.vertex.IVertex; +import com.antgroup.openspg.reasoner.common.graph.vertex.IVertexId; +import com.antgroup.openspg.reasoner.common.graph.vertex.impl.Vertex; +import com.antgroup.openspg.reasoner.graphstate.GraphState; +import com.antgroup.openspg.reasoner.thinker.catalog.ResourceLogicCatalog; +import com.antgroup.openspg.reasoner.thinker.engine.DefaultThinker; +import com.antgroup.openspg.reasoner.thinker.logic.Result; +import com.antgroup.openspg.reasoner.thinker.logic.graph.Entity; +import com.antgroup.openspg.reasoner.thinker.logic.graph.Node; +import com.antgroup.openspg.reasoner.thinker.logic.graph.Predicate; +import java.util.*; +import org.junit.Assert; +import org.junit.Test; + +public class InsuranceTests { + + private GraphState buildGraphState() { + Vertex v1 = GraphUtil.makeVertex("肺钙化", "InsDisease"); + Vertex v2 = GraphUtil.makeVertex("肺部肿物或结节", "InsDisease"); + Vertex v3 = GraphUtil.makeVertex("肺炎性假瘤", "InsDisease"); + Vertex v4 = GraphUtil.makeVertex("肺病", "InsDisease"); + Vertex v5 = GraphUtil.makeVertex("肺癌", "InsDisease"); + Vertex v6 = GraphUtil.makeVertex("既往症", "InsDiseaseDisclaim"); + Vertex v7 = GraphUtil.makeVertex("好医保0免赔", "InsClause"); + Vertex v8 = GraphUtil.makeVertex("好医保0免赔", "InsComProd"); + + Edge e1 = GraphUtil.makeEdge(v1, v2, "child"); + Edge e2 = GraphUtil.makeEdge(v2, v3, "child"); + Edge e3 = GraphUtil.makeEdge(v4, v3, "child"); + Edge e4 = GraphUtil.makeEdge(v4, v5, "child"); + Edge e5 = GraphUtil.makeEdge(v1, v5, "child"); + + Edge e6 = GraphUtil.makeEdge(v2, v2, "evolve"); + Edge e7 = GraphUtil.makeEdge(v5, v5, "evolve"); + Edge e8 = GraphUtil.makeEdge(v3, v2, "evolve"); + + Edge e9 = GraphUtil.makeEdge(v2, v6, "disclaimClause", "disclaimType", "既往"); + Edge e10 = GraphUtil.makeEdge(v6, v7, "clauseVersion"); + Edge e11 = GraphUtil.makeEdge(v7, v8, "insClauseVersion"); + + List> vertexList = Arrays.asList(v1, v2, v3, v4, v5, v6, v7, v8); + List> edgeList = + Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11); + return GraphUtil.buildMemState(vertexList, edgeList); + } + + @Test + public void directEvolve() { + ResourceLogicCatalog logicCatalog = new ResourceLogicCatalog("/InsuranceRules.txt"); + logicCatalog.init(); + Thinker thinker = new DefaultThinker(buildGraphState(), logicCatalog); + + List triples = + thinker.find( + new Entity("肺钙化", "InsDisease"), + new Predicate("directEvolve"), + new Entity("肺病", "InsDisease"), + null); + Assert.assertTrue(triples.size() == 0); + } + + @Test + public void inDirectEvolveForward() { + ResourceLogicCatalog logicCatalog = new ResourceLogicCatalog("/InsuranceRules.txt"); + logicCatalog.init(); + Thinker thinker = new DefaultThinker(buildGraphState(), logicCatalog); + + List triples = + thinker.find( + new Entity("肺钙化", "InsDisease"), + new Predicate("inDirectEvolve"), + new Node("InsDisease"), + new HashMap<>()); + Assert.assertTrue(triples.size() == 3); + } + + @Test + public void childDisease() { + ResourceLogicCatalog logicCatalog = new ResourceLogicCatalog("/InsuranceRules.txt"); + logicCatalog.init(); + Thinker thinker = new DefaultThinker(buildGraphState(), logicCatalog); + + List triples = + thinker.find( + new Entity("肺病", "InsDisease"), + new Predicate("inDirectEvolve"), + new Node("InsDisease"), + new HashMap<>()); + Assert.assertTrue(triples.size() == 2); + } + + @Test + public void disclaim() { + ResourceLogicCatalog logicCatalog = new ResourceLogicCatalog("/InsuranceRules.txt"); + logicCatalog.init(); + Thinker thinker = new DefaultThinker(buildGraphState(), logicCatalog); + + List triples = + thinker.find( + new Entity("肺部肿物或结节", "InsDisease"), + new Predicate("disclaim"), + new Entity("好医保0免赔", "InsComProd"), + new HashMap<>()); + Assert.assertTrue(triples.size() == 1); + } +} diff --git a/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/MedTests.java b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/MedTests.java new file mode 100644 index 000000000..9a5d9023b --- /dev/null +++ b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/MedTests.java @@ -0,0 +1,90 @@ +/* + * 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.thinker; + +import com.antgroup.openspg.reasoner.common.graph.property.IProperty; +import com.antgroup.openspg.reasoner.common.graph.property.impl.VertexVersionProperty; +import com.antgroup.openspg.reasoner.common.graph.vertex.IVertexId; +import com.antgroup.openspg.reasoner.common.graph.vertex.impl.Vertex; +import com.antgroup.openspg.reasoner.graphstate.GraphState; +import com.antgroup.openspg.reasoner.graphstate.impl.MemGraphState; +import com.antgroup.openspg.reasoner.thinker.catalog.ResourceLogicCatalog; +import com.antgroup.openspg.reasoner.thinker.engine.DefaultThinker; +import com.antgroup.openspg.reasoner.thinker.logic.Result; +import com.antgroup.openspg.reasoner.thinker.logic.graph.Entity; +import com.antgroup.openspg.reasoner.thinker.logic.graph.Predicate; +import com.antgroup.openspg.reasoner.thinker.logic.graph.Value; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.Assert; +import org.junit.Test; + +public class MedTests { + private GraphState buildGraphState() { + GraphState graphState = new MemGraphState(); + + Vertex vertex1 = + new Vertex<>( + IVertexId.from("尿酸", "Med.Examination"), + new VertexVersionProperty( + "highExplain", "highExplain desc", "lowExplain", "lowExplain desc")); + graphState.addVertex(vertex1); + + return graphState; + } + + @Test + public void test() { + ResourceLogicCatalog logicCatalog = new ResourceLogicCatalog("/Medical.txt"); + logicCatalog.init(); + Thinker thinker = new DefaultThinker(buildGraphState(), logicCatalog); + // test for normal + Map context = new HashMap<>(); + context.put("population", "男性"); + context.put("value", 460); + List triples = + thinker.find( + new Entity("尿酸", "Med.Examination"), + new Predicate("abnormalRule"), + new Value(), + context); + Assert.assertTrue(triples.size() == 1); + + // test for absent + triples = + thinker.find( + new Entity("尿酸", "Med.Examination"), + new Predicate("abnormalRule"), + new Value(), + new HashMap<>()); + Assert.assertTrue(triples.size() == 4); + } + + @Test + public void testHigh() { + ResourceLogicCatalog logicCatalog = new ResourceLogicCatalog("/Medical.txt"); + logicCatalog.init(); + Thinker thinker = new DefaultThinker(buildGraphState(), logicCatalog); + Map context = new HashMap<>(); + context.put("value", "阳性"); + List triples = + thinker.find( + new Entity("尿酸", "Med.Examination"), + new Predicate("abnormalRule"), + new Value(), + context); + Assert.assertTrue(triples.size() == 2); + } +} diff --git a/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/catalog/ResourceLogicCatalog.java b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/catalog/ResourceLogicCatalog.java new file mode 100644 index 000000000..87cd3a81d --- /dev/null +++ b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/catalog/ResourceLogicCatalog.java @@ -0,0 +1,90 @@ +/* + * 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.thinker.catalog; + +import com.antgroup.openspg.reasoner.lube.catalog.AbstractConnection; +import com.antgroup.openspg.reasoner.lube.catalog.SemanticPropertyGraph; +import com.antgroup.openspg.reasoner.lube.catalog.struct.Field; +import com.antgroup.openspg.reasoner.thinker.SimplifyThinkerParser; +import com.antgroup.openspg.reasoner.thinker.logic.LogicNetwork; +import com.antgroup.openspg.reasoner.thinker.logic.rule.Rule; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.LinkedList; +import java.util.List; +import org.apache.commons.lang3.StringUtils; +import scala.collection.immutable.Map; +import scala.collection.immutable.Set; + +public class ResourceLogicCatalog extends LogicCatalog { + private SimplifyThinkerParser parser; + private String path; + + public ResourceLogicCatalog(String path) { + this.path = path; + this.parser = new SimplifyThinkerParser(); + } + + @Override + public LogicNetwork loadLogicNetwork() { + InputStream inputStream = this.getClass().getResourceAsStream(path); + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); + StringBuilder sb = new StringBuilder(); + List rules = new LinkedList<>(); + try { + String line = null; + while ((line = reader.readLine()) != null) { + if (StringUtils.isNotBlank(line)) { + sb.append(line).append("\n"); + } else { + rules.add(sb.toString()); + sb = new StringBuilder(); + } + } + } catch (IOException ex) { + throw new RuntimeException(ex); + } + if (StringUtils.isNotBlank(sb)) { + rules.add(sb.toString()); + } + LogicNetwork logicNetwork = new LogicNetwork(); + for (String r : rules) { + Rule rule = parser.parseSimplifyDsl(r, null).head(); + logicNetwork.addRule(rule); + } + return logicNetwork; + } + + @Override + public SemanticPropertyGraph getKnowledgeGraph() { + return null; + } + + @Override + public Map> getConnections() { + return null; + } + + @Override + public Set getDefaultNodeProperties() { + return null; + } + + @Override + public Set getDefaultEdgeProperties() { + return null; + } +} diff --git a/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/logic/rule/QlExpressTest.java b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/logic/rule/QlExpressTest.java new file mode 100644 index 000000000..40e6fd597 --- /dev/null +++ b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/logic/rule/QlExpressTest.java @@ -0,0 +1,83 @@ +/* + * 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.thinker.logic.rule; + +import com.antgroup.openspg.reasoner.thinker.qlexpress.QlExpressRunner; +import com.antgroup.openspg.reasoner.udf.rule.RuleRunner; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import org.junit.Assert; +import org.junit.Test; + +public class QlExpressTest { + @Test + public void testNumberCompString() { + RuleRunner runner = QlExpressRunner.getInstance(); + Map context = new HashMap<>(); + context.put("value", "160"); + Assert.assertTrue((Boolean) runner.executeExpression(context, Arrays.asList("value>150"), "")); + } + + @Test + public void testNumberCompString1() { + RuleRunner runner = QlExpressRunner.getInstance(); + Map context = new HashMap<>(); + context.put("value", ">160"); + Assert.assertTrue((Boolean) runner.executeExpression(context, Arrays.asList("value>150"), "")); + } + + @Test + public void testNumberCompString2() { + RuleRunner runner = QlExpressRunner.getInstance(); + Map context = new HashMap<>(); + context.put("value", "<160"); + Assert.assertTrue(runner.executeExpression(context, Arrays.asList("value>150"), "") == null); + } + + @Test + public void testNumberCompString3() { + RuleRunner runner = QlExpressRunner.getInstance(); + Map context = new HashMap<>(); + context.put("value", "<160ng/ml"); + Assert.assertTrue(runner.executeExpression(context, Arrays.asList("value>150"), "") == null); + } + + @Test + public void testNumberCompString4() { + RuleRunner runner = QlExpressRunner.getInstance(); + Map context = new HashMap<>(); + context.put("value", "<0.1234ng/ml"); + Assert.assertTrue((Boolean) runner.executeExpression(context, Arrays.asList("value<0.3"), "")); + } + + @Test + public void testNormal() { + RuleRunner runner = QlExpressRunner.getInstance(); + Map context = new HashMap<>(); + context.put("value", 160); + Assert.assertTrue( + (Boolean) runner.executeExpression(context, Arrays.asList("value > 150"), "")); + } + + @Test + public void testCombination() { + RuleRunner runner = QlExpressRunner.getInstance(); + Map context = new HashMap<>(); + context.put("value", "高"); + Assert.assertTrue( + (Boolean) + runner.executeExpression(context, Arrays.asList("value > 150 || value == '高'"), "")); + } +} diff --git a/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/logic/rule/RuleExecutorTests.java b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/logic/rule/RuleExecutorTests.java index 59c7d80c6..89423e77d 100644 --- a/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/logic/rule/RuleExecutorTests.java +++ b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/logic/rule/RuleExecutorTests.java @@ -46,17 +46,4 @@ public void testRuleExe() { Arrays.asList(new Entity("临床并发症", "高血压分层")), session, new RuleExecutor(), logger); Assert.assertTrue(ret); } - - @Test - public void testRuleExeInContext() { - Rule rule = getR1(); - Node root = rule.getRoot(); - TreeLogger logger = new TreeLogger(root.toString()); - Map session = new HashMap<>(); - session.put("伸缩压", 141); - session.put("症状", "有并发症的糖尿病"); - session.put(new Entity("临床并发症", "高血压分层").toString(), true); - Boolean ret = rule.getRoot().accept(Arrays.asList(), session, new RuleExecutor(), logger); - Assert.assertTrue(ret); - } } diff --git a/reasoner/thinker/src/test/resources/Hypertension.txt b/reasoner/thinker/src/test/resources/Hypertension.txt new file mode 100644 index 000000000..0dd1fb5a1 --- /dev/null +++ b/reasoner/thinker/src/test/resources/Hypertension.txt @@ -0,0 +1,63 @@ +Define (血压水平分级/`1级高血压`) { + R1: 收缩压>=140 or 舒张压>=90 +} + +Define (血压水平分级/`2级高血压`) { + R1: 收缩压>=160 or 舒张压>=100 +} + +Define (血压水平分级/`血压正常高值`) { + R1: 收缩压>=120 or 舒张压>=80 +} + +Define (疾病/`高血压`) { + R1: 血压水平分级/`1级高血压` or 血压水平分级/`2级高血压` +} + +Define (疾病/`肥胖`) { + R1: BMI>=30 +} + +Define (疾病/`慢性肾病3期`) { + R1: GFR>=30 +} + +Define (高血压分层/`心血管危险因素-肥胖`) { + R1: 疾病/`肥胖` + R2: 疾病/`腹型肥胖` +} + +Define (高血压分层/`靶器官损害-肾脏`) { + R1: 疾病/`慢性肾病3期` +} + +Define (高血压分层/`临床并发症-心脏疾病`) { + R1: 疾病/`冠心病` + R2: 疾病/`心力衰竭` + R3: 疾病/`心房颤动` +} + +Define (高血压分层/`心血管危险因素`) { + R1: 高血压分级/`心血管危险因素-肥胖` +} + +Define (高血压分层/`靶器官损害`) { + R1: 高血压分级/`靶器官损害-肾脏` +} + +Define (高血压分层/`临床并发症`) { + R1: 高血压分级/`临床并发症-心脏疾病` +} + +Define (药品/`多药方案`) { + R1: 收缩压>=160 and 舒张压>=100 + R2: 收缩压-目标收缩压上界>=20 +} + +Define ()-[:基本用药方案]->(:药品/`ACEI+噻嗪类利尿剂`) { + R1: 疾病/`高血压` and 药品/`多药方案` +} + +Define ()-[:基本用药方案]->(:药品/`ARB+噻嗪类利尿剂`) { + R1: 疾病/`高血压` and 药品/`多药方案` +} \ No newline at end of file diff --git a/reasoner/thinker/src/test/resources/InsuranceRules.txt b/reasoner/thinker/src/test/resources/InsuranceRules.txt new file mode 100644 index 000000000..b1f4f3623 --- /dev/null +++ b/reasoner/thinker/src/test/resources/InsuranceRules.txt @@ -0,0 +1,16 @@ +Define(a:InsDisease)-[:directEvolve]->(b:InsDisease) { + R1: (a)-[:evolve]->(b) +} + +Define(a:InsDisease)-[:child]->(c:InsDisease) { + R1: (a)-[:child]->(b: InsDisease) AND (b)-[:child]->(c) +} + +Define(a:InsDisease)-[:inDirectEvolve]->(c:InsDisease) { + R1: (a)-[:child]->(b: InsDisease) AND (b)-[:evolve]->(c) +} + +Define(a:InsDisease)-[:disclaim]->(d:InsComProd) { + R1: (a)-[p:disclaimClause]->(b: InsDiseaseDisclaim) AND (b)-[:clauseVersion]->(c:InsClause) AND (c)-[:insClauseVersion]->(d) AND (p.disclaimType == '既往') +} + diff --git a/reasoner/thinker/src/test/resources/Medical.txt b/reasoner/thinker/src/test/resources/Medical.txt new file mode 100644 index 000000000..28bc9606e --- /dev/null +++ b/reasoner/thinker/src/test/resources/Medical.txt @@ -0,0 +1,27 @@ +Define (a:Med.Examination/`尿酸`)-[:abnormalValue]->(c: Med.ExaminationResult/`偏低`) { + R1: contains(population, '男性') AND (value<150 || value in ["低", "阴性"]) +} +Description: "对于男性,尿酸的正常范围是[150-416]umol/L" + +Define (a:Med.Examination/`尿酸`)-[:abnormalValue]->(c: Med.ExaminationResult/`偏高`) { + R1: contains(population, '男性') AND (value>416 || value in ["高", "阳性"]) +} +Description: "对于男性,尿酸的正常范围是[150-416]umol/L" + +Define (a:Med.Examination/`尿酸`)-[:abnormalValue]->(c: Med.ExaminationResult/`偏低`) { + R1: contains(population, '女性') AND (value<89 || value in ["低", "阴性"]) +} +Description: "对于女性,尿酸的正常范围是[89-357]umol/L" + +Define (a:Med.Examination/`尿酸`)-[:abnormalValue]->(c: Med.ExaminationResult/`偏高`) { + R1: contains(population, '女性') AND (value>357 || value in ["高", "阳性"]) +} +Description: "对于女性,尿酸的正常范围是[89-357]umol/L" + +Define (a:Med.Examination)-[:abnormalRule]->(c: string) { + R1: (a)-[:abnormalValue]->(b: Med.ExaminationResult/`偏高`) AND (a)-[: highExplain]->(c) +} + +Define (a:Med.Examination)-[:abnormalRule]->(c: string) { + R1: (a)-[:abnormalValue]->(b: Med.ExaminationResult/`偏低`) AND (a)-[: lowExplain]->(c) +} \ No newline at end of file diff --git a/reasoner/thinker/src/test/scala/com/antgroup/openspg/reasoner/thinker/SimplifyThinkerParserTest.scala b/reasoner/thinker/src/test/scala/com/antgroup/openspg/reasoner/thinker/SimplifyThinkerParserTest.scala index 12be2c394..da4d4ea4a 100644 --- a/reasoner/thinker/src/test/scala/com/antgroup/openspg/reasoner/thinker/SimplifyThinkerParserTest.scala +++ b/reasoner/thinker/src/test/scala/com/antgroup/openspg/reasoner/thinker/SimplifyThinkerParserTest.scala @@ -16,7 +16,7 @@ package com.antgroup.openspg.reasoner.thinker import scala.collection.JavaConverters._ import scala.collection.mutable -import com.antgroup.openspg.reasoner.thinker.logic.graph.{Entity, Predicate} +import com.antgroup.openspg.reasoner.thinker.logic.graph.{Entity, Predicate, Value} import com.antgroup.openspg.reasoner.thinker.logic.graph import com.antgroup.openspg.reasoner.thinker.logic.rule._ import com.antgroup.openspg.reasoner.thinker.logic.rule.exact._ @@ -221,7 +221,7 @@ class SimplifyThinkerParserTest extends AnyFunSpec { new graph.Triple( new Predicate("disclaimClause", "p"), new Predicate("disclaimType"), - new graph.Any())), + new Value(null, "anonymous_3"))), new TriplePattern( new graph.Triple( new graph.Node("InsClause", "c"), @@ -240,7 +240,7 @@ class SimplifyThinkerParserTest extends AnyFunSpec { child .asInstanceOf[QlExpressCondition] .getQlExpress - .equals("hits(get_spo(a, p, b),get_spo(c, anonymous_8, d)) > 2")) + .equals("hits(get_spo(a, p, b),get_spo(c, anonymous_4, d)) > 2")) } }) } @@ -258,4 +258,28 @@ class SimplifyThinkerParserTest extends AnyFunSpec { (entityCount, tripleCount) } + it("define_rule_on_relation_to_concept3") { + val thinkerDsl = + """ + |Define(a:InsDisease)-[:abnormalRule]->(o:String) { + | R1: (a)-[p: disclaimClause]->(b: InsDiseaseDisclaim) AND (b)-[:interpretation]->(o) + |} + |""".stripMargin + val rule: Rule = parser.parseSimplifyDsl(thinkerDsl).head + val head = rule.getHead.asInstanceOf[TriplePattern].getTriple + assert(head.getObject.isInstanceOf[Value]) + val conditionList = rule.getRoot.asInstanceOf[And].getChildren + assert(conditionList.size == 2) + val expectedConditionList = List("get_spo(a,p,b)", "get_spo(b,anonymous_2,o)") + conditionList.containsAll(expectedConditionList.asJava) + assert(rule.getBody.size() == 2) + rule.getBody.asScala.foreach(clause => { + val triple = clause.asInstanceOf[TriplePattern].getTriple + if (triple.getSubject.alias().equals("b")) { + assert(triple.getPredicate.alias().equals("anonymous_2")) + assert(triple.getObject.alias().equals("o")) + } + }) + + } } 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 e67633cfd..ca70000cc 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 @@ -19,11 +19,7 @@ import com.antgroup.openspg.reasoner.udf.UdfMngFactory; import com.antgroup.openspg.reasoner.udf.model.RuntimeUdfMeta; import com.antgroup.openspg.reasoner.udf.model.UdfOperatorTypeEnum; -import com.antgroup.openspg.reasoner.udf.rule.op.OperatorEqualsLessMore; -import com.antgroup.openspg.reasoner.udf.rule.op.OperatorGetValue; -import com.antgroup.openspg.reasoner.udf.rule.op.OperatorIn; -import com.antgroup.openspg.reasoner.udf.rule.op.OperatorLike; -import com.antgroup.openspg.reasoner.udf.rule.op.OperatorMultiDiv; +import com.antgroup.openspg.reasoner.udf.rule.op.*; import com.antgroup.openspg.reasoner.udf.rule.udf.UdfWrapper; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; @@ -33,12 +29,7 @@ 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.*; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,7 +41,7 @@ public class RuleRunner { private static final Cache> contextCache = CacheBuilder.newBuilder().maximumSize(100).expireAfterWrite(24, TimeUnit.HOURS).build(); - private final ExpressRunner EXPRESS_RUNNER = new ExpressRunner(); + protected final ExpressRunner EXPRESS_RUNNER = new ExpressRunner(); private static final Set keywordSet = new HashSet<>(); @@ -155,7 +146,7 @@ public Object executeExpression( return null; } - private RuleRunner() {} + protected RuleRunner() {} private static volatile RuleRunner instance = null; @@ -173,7 +164,7 @@ public static RuleRunner getInstance() { return instance; } - private void init() { + protected void init() { // disable print error // InstructionSet.printInstructionError = false; // use short circuit @@ -181,6 +172,7 @@ private void init() { registerUdf(); overrideOperator(); EXPRESS_RUNNER.addFunction("get_value", new OperatorGetValue()); + EXPRESS_RUNNER.addFunction("get_spo", new OperatorGetSPO()); } /** register all udfs */ @@ -207,7 +199,7 @@ private void registerUdf() { } } - private void overrideOperator() { + protected void overrideOperator() { Lists.newArrayList( new Tuple2("<", new OperatorEqualsLessMore("<")), new Tuple2(">", new OperatorEqualsLessMore(">")), diff --git a/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/rule/op/OperatorEqualsLessMore.java b/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/rule/op/OperatorEqualsLessMore.java index 50f260c45..4fb9bab28 100644 --- a/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/rule/op/OperatorEqualsLessMore.java +++ b/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/rule/op/OperatorEqualsLessMore.java @@ -98,13 +98,13 @@ public static Integer compareDataWithoutException(Object op1, Object op2) { return compareResult; } - private static final int NUMBER_TYPE_BYTE = 1; - private static final int NUMBER_TYPE_SHORT = 2; - private static final int NUMBER_TYPE_INT = 3; - private static final int NUMBER_TYPE_LONG = 4; - private static final int NUMBER_TYPE_FLOAT = 5; - private static final int NUMBER_TYPE_DOUBLE = 6; - private static final int NUMBER_TYPE_DECIMAL = 7; + protected static final int NUMBER_TYPE_BYTE = 1; + protected static final int NUMBER_TYPE_SHORT = 2; + protected static final int NUMBER_TYPE_INT = 3; + protected static final int NUMBER_TYPE_LONG = 4; + protected static final int NUMBER_TYPE_FLOAT = 5; + protected static final int NUMBER_TYPE_DOUBLE = 6; + protected static final int NUMBER_TYPE_DECIMAL = 7; public static int getSeq(Class aClass) { if (aClass == Byte.class || aClass == byte.class) { diff --git a/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/rule/op/OperatorGetSPO.java b/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/rule/op/OperatorGetSPO.java new file mode 100644 index 000000000..2f8ca53df --- /dev/null +++ b/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/rule/op/OperatorGetSPO.java @@ -0,0 +1,36 @@ +/* + * 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.udf.rule.op; + +import com.ql.util.express.ArraySwap; +import com.ql.util.express.InstructionSetContext; +import com.ql.util.express.OperateData; +import com.ql.util.express.instruction.OperateDataCacheManager; +import com.ql.util.express.instruction.op.OperatorBase; +import com.ql.util.express.instruction.opdata.OperateDataAttr; +import org.apache.commons.lang3.StringUtils; + +public class OperatorGetSPO extends OperatorBase { + @Override + public OperateData executeInner(InstructionSetContext parent, ArraySwap list) throws Exception { + Object[] parameterNames = new Object[list.length]; + + for (int i = 0; i < list.length; ++i) { + parameterNames[i] = ((OperateDataAttr) list.get(i)).getName(); + } + String key = StringUtils.join(parameterNames, "_"); + Object result = parent.getParent().get(key); + return OperateDataCacheManager.fetchOperateData(result, Object.class); + } +} From 408b6e07bf4a61a4725d496b25d24f4de5d1beba Mon Sep 17 00:00:00 2001 From: wangshaofei Date: Mon, 1 Jul 2024 11:28:18 +0800 Subject: [PATCH 12/29] feat(reasoner): upgrade thinker dsl (#314) --- .../com/antgroup/openspg/reasoner/KGDSL.g4 | 6 +- .../logic/graph/CombinationEntity.java | 92 +++++++++++++++++++ .../reasoner/thinker/ThinkerRuleParser.scala | 53 ++++++----- .../thinker/SimplifyThinkerParserTest.scala | 34 ++++++- 4 files changed, 160 insertions(+), 25 deletions(-) create mode 100644 reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/CombinationEntity.java diff --git a/reasoner/kgdsl-parser/src/main/antlr4/com/antgroup/openspg/reasoner/KGDSL.g4 b/reasoner/kgdsl-parser/src/main/antlr4/com/antgroup/openspg/reasoner/KGDSL.g4 index b3e9c09ad..9a860da98 100644 --- a/reasoner/kgdsl-parser/src/main/antlr4/com/antgroup/openspg/reasoner/KGDSL.g4 +++ b/reasoner/kgdsl-parser/src/main/antlr4/com/antgroup/openspg/reasoner/KGDSL.g4 @@ -208,7 +208,8 @@ element_variable_declaration : element_variable ; element_variable : identifier ; label_expression_lookup: vertical_bar label_name; label_expression : label_name label_expression_lookup* ; -label_name : entity_type | concept_name; +combination_concept : concept_name plus_sign concept_name (plus_sign concept_name)*; +label_name : entity_type | concept_name | combination_concept; entity_type : identifier | prefix_name; prefix_name : identifier period identifier ; concept_name : meta_concept_type solidus concept_instance_id ; @@ -1015,7 +1016,8 @@ description: the_description_symbol colon unbroken_character_string_literal; rule_and_action_body: left_brace rule_body_content (action_body_structure)? right_brace; -rule_body_content : (identifier explain? colon logical_statement)*; +rule_prefix: identifier explain? colon; +rule_body_content : rule_prefix? logical_statement (rule_prefix logical_statement)*; logical_statement : value_expression; diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/CombinationEntity.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/CombinationEntity.java new file mode 100644 index 000000000..08a7d560c --- /dev/null +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/CombinationEntity.java @@ -0,0 +1,92 @@ +/* + * 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.thinker.logic.graph; + +import com.alibaba.fastjson.JSON; +import java.util.List; +import java.util.Objects; + +public class CombinationEntity extends Element { + private List entityList; + private String alias; + + public CombinationEntity() {} + + public CombinationEntity(List entityList) { + this.entityList = entityList; + } + + public CombinationEntity(List entityList, String alias) { + this.entityList = entityList; + this.alias = alias; + } + + /** + * Getter method for property entityList. + * + * @return property value of entityList + */ + public List getEntityList() { + return entityList; + } + + /** + * Setter method for property entityList. + * + * @param entityList value to be assigned to property entityList + */ + public void setEntityList(List entityList) { + this.entityList = entityList; + } + + /** + * Getter method for property alias. + * + * @return property value of alias + */ + public String getAlias() { + return alias; + } + + /** + * Setter method for property alias. + * + * @param alias value to be assigned to property alias + */ + public void setAlias(String alias) { + this.alias = alias; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof CombinationEntity)) { + return false; + } + CombinationEntity that = (CombinationEntity) o; + return Objects.equals(entityList, that.entityList); + } + + @Override + public int hashCode() { + return Objects.hash(entityList, alias); + } + + @Override + public String toString() { + return JSON.toJSONString(entityList); + } +} diff --git a/reasoner/thinker/src/main/scala/com/antgroup/openspg/reasoner/thinker/ThinkerRuleParser.scala b/reasoner/thinker/src/main/scala/com/antgroup/openspg/reasoner/thinker/ThinkerRuleParser.scala index f3a28e0e5..f9caf0f72 100644 --- a/reasoner/thinker/src/main/scala/com/antgroup/openspg/reasoner/thinker/ThinkerRuleParser.scala +++ b/reasoner/thinker/src/main/scala/com/antgroup/openspg/reasoner/thinker/ThinkerRuleParser.scala @@ -13,39 +13,30 @@ package com.antgroup.openspg.reasoner.thinker -import java.util.Locale - -import scala.collection.JavaConverters._ -import scala.collection.mutable -import scala.collection.mutable.ListBuffer - import com.antgroup.openspg.reasoner.KGDSLParser._ -import com.antgroup.openspg.reasoner.lube.common.expr.{ - BEqual, - BinaryOpExpr, - BNotEqual, - ConceptExpr, - Expr, - TripleExpr -} +import com.antgroup.openspg.reasoner.lube.common.expr._ import com.antgroup.openspg.reasoner.lube.utils.transformer.impl.Expr2QlexpressTransformer import com.antgroup.openspg.reasoner.parser.expr.RuleExprParser -import com.antgroup.openspg.reasoner.thinker.logic.graph.{Element, Entity, Predicate, Value} import com.antgroup.openspg.reasoner.thinker.logic.graph +import com.antgroup.openspg.reasoner.thinker.logic.graph.{ + CombinationEntity, + Element, + Entity, + Predicate, + Value +} import com.antgroup.openspg.reasoner.thinker.logic.rule.{ ClauseEntry, EntityPattern, Node, TriplePattern } -import com.antgroup.openspg.reasoner.thinker.logic.rule.exact.{ - And, - Condition, - Not, - Or, - QlExpressCondition -} +import com.antgroup.openspg.reasoner.thinker.logic.rule.exact.{Not, _} +import java.util.Locale import org.apache.commons.lang3.StringUtils +import scala.collection.JavaConverters._ +import scala.collection.mutable +import scala.collection.mutable.ListBuffer class ThinkerRuleParser extends RuleExprParser { val expr2StringTransformer = new Expr2QlexpressTransformer() @@ -353,6 +344,24 @@ class ThinkerRuleParser extends RuleExprParser { } else if (valueTypeSet.contains(sType.toUpperCase())) { sNode = new Value(null, sAlias) } + val combinationConceptContext = elementPatternDeclaration + .element_lookup() + .label_expression() + .label_name() + .combination_concept() + if (combinationConceptContext != null) { + val conceptEntityList = new mutable.ListBuffer[Entity]() + combinationConceptContext + .concept_name() + .asScala + .foreach(concept => { + val conceptEntity = constructConceptEntity(concept) + conceptEntityList += conceptEntity + }) + val combinationEntity = new CombinationEntity(conceptEntityList.asJava) + combinationEntity.setAlias(sAlias) + sNode = combinationEntity + } } aliasToElementMap += (sAlias -> sNode) sNode diff --git a/reasoner/thinker/src/test/scala/com/antgroup/openspg/reasoner/thinker/SimplifyThinkerParserTest.scala b/reasoner/thinker/src/test/scala/com/antgroup/openspg/reasoner/thinker/SimplifyThinkerParserTest.scala index da4d4ea4a..c75d3de59 100644 --- a/reasoner/thinker/src/test/scala/com/antgroup/openspg/reasoner/thinker/SimplifyThinkerParserTest.scala +++ b/reasoner/thinker/src/test/scala/com/antgroup/openspg/reasoner/thinker/SimplifyThinkerParserTest.scala @@ -16,7 +16,13 @@ package com.antgroup.openspg.reasoner.thinker import scala.collection.JavaConverters._ import scala.collection.mutable -import com.antgroup.openspg.reasoner.thinker.logic.graph.{Entity, Predicate, Value} +import com.antgroup.openspg.reasoner.thinker.logic.graph.{ + CombinationEntity, + Entity, + Predicate, + Triple, + Value +} import com.antgroup.openspg.reasoner.thinker.logic.graph import com.antgroup.openspg.reasoner.thinker.logic.rule._ import com.antgroup.openspg.reasoner.thinker.logic.rule.exact._ @@ -258,6 +264,32 @@ class SimplifyThinkerParserTest extends AnyFunSpec { (entityCount, tripleCount) } + it("test combination concept") { + val thinkerDsl = + """ + |Define (:Med.drug)-[:联合用药方案]->(:药品/`噻嗪类利尿剂`+药品/`脂脉康胶囊`+药品/`藏青果颗粒`) { + | R1: 疾病/`高血压` and 药品/`多药方案` + |} + |""".stripMargin + val rule: Rule = parser.parseSimplifyDsl(thinkerDsl).head + assert(rule.getHead.isInstanceOf[TriplePattern]) + val triple: Triple = rule.getHead.asInstanceOf[TriplePattern].getTriple + assert(triple.getObject.isInstanceOf[CombinationEntity]) + val entityList = triple.getObject.asInstanceOf[CombinationEntity].getEntityList + assert(entityList.size() == 3) + } + + it("test remove first rule_prefix") { + val thinkerDsl = + """ + |Define(a:InsDisease)-[:disclaim]->(d:InsComProd) { + | 疾病/`高血压` and 疾病/`低血压` + |} + |""".stripMargin + val ruleList: List[Rule] = parser.parseSimplifyDsl(thinkerDsl) + assert(ruleList.size == 1) + } + it("define_rule_on_relation_to_concept3") { val thinkerDsl = """ From 097b127f200629b6041f1ac7c3bb6e3889089f2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=9F=B9=E9=BE=99?= Date: Mon, 1 Jul 2024 12:04:13 +0800 Subject: [PATCH 13/29] fix(reasoner): fix bugs for concept expand & add runtime trace data (#278) --- .../common/utils/LabelTypeUtils.scala | 31 ++ .../reasoner/lube/catalog/Catalog.scala | 3 +- .../lube/catalog/SemanticPropertyGraph.scala | 26 +- .../lube/common/pattern/Element.scala | 11 +- .../lube/common/pattern/Pattern.scala | 5 +- .../rules/ConvertToMetaConcept.scala | 13 +- .../logical/planning/SubQueryMerger.scala | 15 +- .../logical/planning/SubQueryPlanner.scala | 24 +- .../semantic/rules/MetaConceptExplain.scala | 11 +- .../reasoner/runner/local/rdg/LocalRDG.java | 88 +++- .../runner/local/LocalRunnerTest.java | 425 ++++++------------ .../loader/TestFanxiqianGraphLoader.java | 43 ++ .../KgReasonerAddEdgeWithPropertyTest.java | 132 ------ .../local/main/KgReasonerZijinLocalTest.java | 200 --------- .../main/group/concept/GroupConceptTest.java | 16 +- .../reasoner/pattern/PatternMatcher.java | 63 ++- .../reasoner/recorder/DefaultRecorder.java | 7 +- .../reasoner/recorder/EmptyRecorder.java | 7 +- .../reasoner/recorder/IExecutionRecorder.java | 7 +- .../recorder/action/DebugInfoWithRule.java | 45 ++ .../recorder/action/DebugInfoWithStartId.java | 31 +- .../recorder/action/SampleAction.java | 66 ++- .../reasoner/recorder/action/SubAction.java | 3 + .../openspg/reasoner/utils/RunnerUtil.java | 3 + .../openspg/reasoner/util/LoaderUtil.scala | 27 +- .../warehouse/utils/WareHouseUtils.java | 25 ++ 26 files changed, 579 insertions(+), 748 deletions(-) create mode 100644 reasoner/common/src/main/scala/com/antgroup/openspg/reasoner/common/utils/LabelTypeUtils.scala create mode 100644 reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/loader/TestFanxiqianGraphLoader.java delete mode 100644 reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/KgReasonerAddEdgeWithPropertyTest.java create mode 100644 reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/DebugInfoWithRule.java diff --git a/reasoner/common/src/main/scala/com/antgroup/openspg/reasoner/common/utils/LabelTypeUtils.scala b/reasoner/common/src/main/scala/com/antgroup/openspg/reasoner/common/utils/LabelTypeUtils.scala new file mode 100644 index 000000000..2d9d643e1 --- /dev/null +++ b/reasoner/common/src/main/scala/com/antgroup/openspg/reasoner/common/utils/LabelTypeUtils.scala @@ -0,0 +1,31 @@ +/* + * 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 + +object LabelTypeUtils { + + /** + * get label meta type + * @param sType + * @return + */ + def getMetaType(sType: String): String = { + if (sType.contains("/")) { + sType.split("/")(0) + } else { + sType + } + } + +} diff --git a/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/catalog/Catalog.scala b/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/catalog/Catalog.scala index 955a5d596..852c949dc 100644 --- a/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/catalog/Catalog.scala +++ b/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/catalog/Catalog.scala @@ -16,6 +16,7 @@ package com.antgroup.openspg.reasoner.lube.catalog import scala.collection.mutable import com.antgroup.openspg.reasoner.common.exception.{ConnectionNotFoundException, GraphAlreadyExistsException, GraphNotFoundException} +import com.antgroup.openspg.reasoner.common.utils.LabelTypeUtils import com.antgroup.openspg.reasoner.lube.catalog.struct.Field import com.antgroup.openspg.reasoner.lube.common.graph.IRGraph import com.antgroup.openspg.reasoner.udf.{UdfMng, UdfMngFactory} @@ -80,7 +81,7 @@ abstract class Catalog() extends Serializable { * @return */ def getConnection(typeName: String): Set[AbstractConnection] = { - val finalType = typeName.split("/")(0) + val finalType = LabelTypeUtils.getMetaType(typeName) if (!connections.contains(finalType)) { throw ConnectionNotFoundException(s"$finalType not found.", null) } diff --git a/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/catalog/SemanticPropertyGraph.scala b/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/catalog/SemanticPropertyGraph.scala index cdfb765ae..28f094149 100644 --- a/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/catalog/SemanticPropertyGraph.scala +++ b/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/catalog/SemanticPropertyGraph.scala @@ -18,6 +18,7 @@ import scala.collection.mutable import com.antgroup.openspg.reasoner.common.exception.NotDefineException import com.antgroup.openspg.reasoner.common.graph.edge.{Direction, SPO} import com.antgroup.openspg.reasoner.common.types.{KgType, KTString} +import com.antgroup.openspg.reasoner.common.utils.LabelTypeUtils import com.antgroup.openspg.reasoner.lube.catalog.struct.{Edge, Field, Node, NodeType} /** @@ -63,27 +64,32 @@ class SemanticPropertyGraph( } } + def getEdgeWithMetaType(spoStr: String): SPO = { + var spo = new SPO(spoStr) + if (!graphSchema.edges.contains(spo)) { + spo = new SPO(LabelTypeUtils.getMetaType(spo.getS), + spo.getP, LabelTypeUtils.getMetaType(spo.getO)) + } + spo + } + def getNode(nodeLabel: String): Node = { - graphSchema.nodes(nodeLabel) + val nodeLabelOnlyMetaType = LabelTypeUtils.getMetaType(nodeLabel) + graphSchema.nodes(nodeLabelOnlyMetaType) } def getEdge(spoStr: String): Edge = { - var spo = new SPO(spoStr) - if (spo.getP.equals("belongTo") && !graphSchema.edges.contains(spo)) { - spo = new SPO(spo.getS, spo.getP, spo.getO.split("/")(0)) - } + val spo = getEdgeWithMetaType(spoStr) graphSchema.edges(spo) } def containsNode(nodeLabel: String): Boolean = { - graphSchema.nodes.contains(nodeLabel) + val nodeLabelOnlyMetaType = LabelTypeUtils.getMetaType(nodeLabel) + graphSchema.nodes.contains(nodeLabelOnlyMetaType) } def containsEdge(spoStr: String): Boolean = { - var spo = new SPO(spoStr) - if (spo.getP.equals("belongTo") && !graphSchema.edges.contains(spo)) { - spo = new SPO(spo.getS, spo.getP, spo.getO.split("/")(0)) - } + val spo = getEdgeWithMetaType(spoStr) graphSchema.edges.contains(spo) } diff --git a/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/common/pattern/Element.scala b/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/common/pattern/Element.scala index e3deb061d..7eb7c2496 100644 --- a/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/common/pattern/Element.scala +++ b/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/common/pattern/Element.scala @@ -15,6 +15,8 @@ package com.antgroup.openspg.reasoner.lube.common.pattern import scala.language.implicitConversions +import com.antgroup.openspg.reasoner.common.types.KTString +import com.antgroup.openspg.reasoner.common.utils.LabelTypeUtils import com.antgroup.openspg.reasoner.lube.common.expr._ import com.antgroup.openspg.reasoner.lube.common.rule.{LogicRule, Rule} @@ -37,6 +39,11 @@ trait Element extends Serializable { */ case class PatternElement(alias: String, typeNames: Set[String], var rule: Rule) extends Element { + def getMetaTypeNames: Set[String] = { + typeNames.map(x => { + LabelTypeUtils.getMetaType(x) + }) + } override def toString: String = { val stringBuilder = StringBuilder.newBuilder stringBuilder.append("(").append(alias).append(":") @@ -71,7 +78,9 @@ object ElementOps { implicit def toPattenElement(element: Element): PatternElement = { element match { case EntityElement(id, label, alias) => - val rule = BinaryOpExpr(BEqual, UnaryOpExpr(GetField("id"), Ref(alias)), VString(id)) + val rule = FunctionExpr("contains_any", + List.apply(UnaryOpExpr(GetField("id"), + Ref(alias)), VList(List.apply(id), KTString))) PatternElement(alias, Set.apply(label), LogicRule(s"R_$alias", "", rule)) case patternElement: PatternElement => patternElement } diff --git a/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/common/pattern/Pattern.scala b/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/common/pattern/Pattern.scala index b6e180803..aa38a4277 100644 --- a/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/common/pattern/Pattern.scala +++ b/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/common/pattern/Pattern.scala @@ -16,6 +16,7 @@ package com.antgroup.openspg.reasoner.lube.common.pattern import scala.collection.mutable import com.antgroup.openspg.reasoner.common.graph.edge.{Direction, SPO} +import com.antgroup.openspg.reasoner.common.utils.LabelTypeUtils import com.antgroup.openspg.reasoner.lube.catalog.SemanticPropertyGraph sealed trait Pattern { @@ -162,9 +163,7 @@ case class GraphPattern( if (label.equals(compareLabel)) { return label } - if (nodeLabel.contains("/")) { - compareLabel = nodeLabel.split("/")(0) - } + compareLabel = LabelTypeUtils.getMetaType(nodeLabel) if (label.equals(compareLabel)) { return nodeLabel } diff --git a/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/optimizer/rules/ConvertToMetaConcept.scala b/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/optimizer/rules/ConvertToMetaConcept.scala index f5135e53d..174b769eb 100644 --- a/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/optimizer/rules/ConvertToMetaConcept.scala +++ b/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/optimizer/rules/ConvertToMetaConcept.scala @@ -13,15 +13,18 @@ package com.antgroup.openspg.reasoner.lube.logical.optimizer.rules +import scala.collection.mutable + import com.antgroup.openspg.reasoner.common.exception.InvalidGraphException import com.antgroup.openspg.reasoner.common.types.KTString -import com.antgroup.openspg.reasoner.lube.common.expr.{BinaryOpExpr, _} +import com.antgroup.openspg.reasoner.common.utils.LabelTypeUtils +import com.antgroup.openspg.reasoner.lube.common.expr._ import com.antgroup.openspg.reasoner.lube.common.pattern.{NodePattern, PartialGraphPattern, PatternElement} import com.antgroup.openspg.reasoner.lube.common.rule.LogicRule import com.antgroup.openspg.reasoner.lube.logical.operators.{ExpandInto, LogicalOperator, PatternScan} import com.antgroup.openspg.reasoner.lube.logical.optimizer.{Direction, SimpleRule, Up} import com.antgroup.openspg.reasoner.lube.logical.planning.LogicalPlannerContext -import scala.collection.mutable + object ConvertToMetaConcept extends SimpleRule { @@ -71,7 +74,7 @@ object ConvertToMetaConcept extends SimpleRule { if (types.isEmpty) { patternElement } else { - val metaConcepts = types.map(_.split("/").head) + val metaConcepts = types.map(LabelTypeUtils.getMetaType(_)) if (metaConcepts.size > 1) { throw InvalidGraphException("Entities must belong to the same meta concept") } @@ -80,7 +83,9 @@ object ConvertToMetaConcept extends SimpleRule { LogicRule( "metaConceptRule", "belongToConcept", - BinaryOpExpr(BIn, Ref(patternElement.alias + ".id"), VList(Ids.toList, KTString))) + FunctionExpr("contains_any", + List.apply(UnaryOpExpr(GetField("id"), + Ref(patternElement.alias)), VList(Ids.toList, KTString)))) patternElement.copy(typeNames = metaConcepts, rule = rule) } } diff --git a/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/planning/SubQueryMerger.scala b/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/planning/SubQueryMerger.scala index fa56b807f..0b2da8ee1 100644 --- a/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/planning/SubQueryMerger.scala +++ b/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/planning/SubQueryMerger.scala @@ -17,6 +17,7 @@ import scala.collection.mutable import com.antgroup.openspg.reasoner.common.graph.edge.{Direction, SPO} import com.antgroup.openspg.reasoner.common.trees.TopDown +import com.antgroup.openspg.reasoner.common.utils.LabelTypeUtils import com.antgroup.openspg.reasoner.lube.catalog.Catalog import com.antgroup.openspg.reasoner.lube.common.pattern.Pattern import com.antgroup.openspg.reasoner.lube.logical.{EdgeVar, NodeVar, RepeatPathVar, SolvedModel, Var} @@ -90,7 +91,7 @@ class SubQueryMerger(val dag: Dag[LogicalOperator])(implicit context: LogicalPla if (conn.direction == Direction.OUT) pattern.getNode(conn.target).typeNames else pattern.getNode(conn.source).typeNames conn.relTypes.contains(spo.getP) && (types.contains( - getMetaType(spo.getO)) || types.contains(spo.getO)) + LabelTypeUtils.getMetaType(spo.getO)) || types.contains(spo.getO)) }) .head .direction @@ -112,12 +113,12 @@ class SubQueryMerger(val dag: Dag[LogicalOperator])(implicit context: LogicalPla conn.relTypes.contains(spo.getP) && pattern .getNode(conn.target) .typeNames - .contains(getMetaType(spo.getO)) + .contains(LabelTypeUtils.getMetaType(spo.getO)) } else { conn.relTypes.contains(spo.getP) && pattern .getNode(conn.source) .typeNames - .contains(getMetaType(spo.getO)) + .contains(LabelTypeUtils.getMetaType(spo.getO)) } }) .head @@ -131,12 +132,4 @@ class SubQueryMerger(val dag: Dag[LogicalOperator])(implicit context: LogicalPla defined.toSet } - private def getMetaType(sType: String) = { - if (sType.contains("/")) { - sType.split("/")(0) - } else { - sType - } - } - } diff --git a/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/planning/SubQueryPlanner.scala b/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/planning/SubQueryPlanner.scala index 9bae3fa1c..914a46a7e 100644 --- a/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/planning/SubQueryPlanner.scala +++ b/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/planning/SubQueryPlanner.scala @@ -17,18 +17,12 @@ import scala.collection.mutable import com.antgroup.openspg.reasoner.common.constants.Constants import com.antgroup.openspg.reasoner.common.graph.edge.{Direction, SPO} -import com.antgroup.openspg.reasoner.common.utils.ParameterUtils +import com.antgroup.openspg.reasoner.common.utils.{LabelTypeUtils, ParameterUtils} import com.antgroup.openspg.reasoner.lube.Logging import com.antgroup.openspg.reasoner.lube.block._ import com.antgroup.openspg.reasoner.lube.catalog.{Catalog, SemanticRule, TemplateSemanticRule} import com.antgroup.openspg.reasoner.lube.common.pattern.Pattern -import com.antgroup.openspg.reasoner.lube.logical.{ - EdgeVar, - NodeVar, - RepeatPathVar, - SolvedModel, - Var -} +import com.antgroup.openspg.reasoner.lube.logical.{EdgeVar, NodeVar, RepeatPathVar, SolvedModel, Var} import com.antgroup.openspg.reasoner.lube.logical.operators._ import com.antgroup.openspg.reasoner.lube.logical.planning.SubQueryPlanner.nodeName import com.antgroup.openspg.reasoner.lube.logical.validate.Dag @@ -157,7 +151,7 @@ class SubQueryPlanner(val dag: Dag[Block])(implicit context: LogicalPlannerConte if (conn.direction == Direction.OUT) pattern.getNode(conn.target).typeNames else pattern.getNode(conn.source).typeNames conn.relTypes.contains(spo.getP) && (types.contains( - getMetaType(spo.getO)) || types.contains(spo.getO)) + LabelTypeUtils.getMetaType(spo.getO)) || types.contains(spo.getO)) }) .head .direction @@ -179,12 +173,12 @@ class SubQueryPlanner(val dag: Dag[Block])(implicit context: LogicalPlannerConte conn.relTypes.contains(spo.getP) && pattern .getNode(conn.target) .typeNames - .contains(getMetaType(spo.getO)) + .contains(LabelTypeUtils.getMetaType(spo.getO)) } else { conn.relTypes.contains(spo.getP) && pattern .getNode(conn.source) .typeNames - .contains(getMetaType(spo.getO)) + .contains(LabelTypeUtils.getMetaType(spo.getO)) } }) .head @@ -198,14 +192,6 @@ class SubQueryPlanner(val dag: Dag[Block])(implicit context: LogicalPlannerConte defined.toSet } - private def getMetaType(sType: String) = { - if (sType.contains("/")) { - sType.split("/")(0) - } else { - sType - } - } - private def rewriteBlock(block: Block, startType: String, direction: Direction): Block = { val rewriteBlock = rewriteBlockDirection(block, direction) val alias = getRootAlias(rewriteBlock, startType, direction) diff --git a/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/validate/semantic/rules/MetaConceptExplain.scala b/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/validate/semantic/rules/MetaConceptExplain.scala index ae95e68b6..100775573 100644 --- a/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/validate/semantic/rules/MetaConceptExplain.scala +++ b/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/validate/semantic/rules/MetaConceptExplain.scala @@ -14,13 +14,8 @@ package com.antgroup.openspg.reasoner.lube.logical.validate.semantic.rules import com.antgroup.openspg.reasoner.lube.block.{Block, MatchBlock} -import com.antgroup.openspg.reasoner.lube.catalog.SemanticPropertyGraph -import com.antgroup.openspg.reasoner.lube.common.pattern.{ - Connection, - EntityElement, - GraphPattern, - PatternElement -} +import com.antgroup.openspg.reasoner.lube.catalog.{Catalog, SemanticPropertyGraph} +import com.antgroup.openspg.reasoner.lube.common.pattern.{Connection, EntityElement, GraphPattern, PatternElement} import com.antgroup.openspg.reasoner.lube.logical.planning.LogicalPlannerContext import com.antgroup.openspg.reasoner.lube.logical.validate.semantic.Explain import scala.collection.mutable @@ -40,7 +35,7 @@ object MetaConceptExplain extends Explain { if (metaConceptEdges.isEmpty) { p } else { - val kg = context.catalog.getKnowledgeGraph(); + val kg = context.catalog.getGraph(Catalog.defaultGraphName) val metaConceptMap: mutable.HashMap[String, Set[String]] = mutable.HashMap.empty metaConceptEdges.foreach(e => parseMetaConcept(kg, e, pattern, metaConceptMap)) val newNodes = pattern.nodes.map(n => diff --git a/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/rdg/LocalRDG.java b/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/rdg/LocalRDG.java index 0ac76b1d5..068bdcfac 100644 --- a/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/rdg/LocalRDG.java +++ b/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/rdg/LocalRDG.java @@ -27,6 +27,7 @@ import com.antgroup.openspg.reasoner.common.graph.vertex.IVertex; import com.antgroup.openspg.reasoner.common.graph.vertex.IVertexId; import com.antgroup.openspg.reasoner.common.graph.vertex.impl.Vertex; +import com.antgroup.openspg.reasoner.common.utils.LabelTypeUtils; import com.antgroup.openspg.reasoner.graphstate.GraphState; import com.antgroup.openspg.reasoner.graphstate.model.MergeTypeEnum; import com.antgroup.openspg.reasoner.kggraph.KgGraph; @@ -180,11 +181,61 @@ private java.util.Set getStartId(java.util.List> k return startIdSet; } - private java.util.Map> generateRuntimeLog( + private java.util.Map getStartIdWithHitRuleValue( + java.util.List> kgGraphList, java.util.List rules) { + java.util.Map startIdSet = new java.util.HashMap(); + java.util.List>> fields = new ArrayList<>(); + for (Rule rule : rules) { + fields.addAll(WareHouseUtils.getRuleUsedAliasEle(rule)); + } + for (KgGraph kgGraph : kgGraphList) { + java.util.Map context = + RunnerUtil.kgGraph2Context(RunnerUtil.getKgGraphInitContext(this.kgGraphSchema), kgGraph); + StringBuilder sb = new StringBuilder(""); + for (Tuple2> field : fields) { + Object value = context.get(field._1); + if (!(value instanceof java.util.Map)) { + continue; + } + java.util.Map values = + (java.util.Map) context.get(field._1); + for (String prop : field._2) { + if (!values.containsKey(prop)) { + continue; + } + sb.append(field._1).append(".").append(prop).append("=").append(values.get(prop)); + } + } + System.out.println(sb.toString()); + java.util.List> startVertexList = + kgGraph.getVertex(this.curRdgStartVertexAlias); + for (IVertex startId : startVertexList) { + startIdSet.put(startId.getId(), sb.toString()); + } + startVertexList = kgGraph.getVertex(this.startVertexAlias); + for (IVertex startId : startVertexList) { + startIdSet.put(startId.getId(), sb.toString()); + } + } + return startIdSet; + } + + private java.util.Map generateRuntimeLog( java.util.Set failedStartId, java.util.Set successStartId) { - java.util.Map> runtimeLogMap = new HashMap<>(); + java.util.Map runtimeLogMap = new HashMap<>(); + runtimeLogMap.put(SampleAction.FAILED_START_ID_KEY, Lists.newArrayList(failedStartId)); + runtimeLogMap.put(SampleAction.PASS_START_ID_KEY, Lists.newArrayList(successStartId)); + return runtimeLogMap; + } + + private java.util.Map generateRuntimeLog( + java.util.Set failedStartId, + java.util.Set successStartId, + java.util.Map ruleRuntimeValue) { + java.util.Map runtimeLogMap = new HashMap<>(); runtimeLogMap.put(SampleAction.FAILED_START_ID_KEY, Lists.newArrayList(failedStartId)); runtimeLogMap.put(SampleAction.PASS_START_ID_KEY, Lists.newArrayList(successStartId)); + runtimeLogMap.put(SampleAction.RULE_RUNTIME_VALUE, ruleRuntimeValue); return runtimeLogMap; } @@ -218,6 +269,7 @@ public LocalRDG( this.startVertexAlias = startVertexAlias; this.taskId = taskId; this.patternMatcher = new PatternMatcher(this.taskId, graphState); + this.patternMatcher.setDebugEnable(true); this.isCarryTraversalGraph = carryTraversalGraph; if (null == executionRecorder) { @@ -314,6 +366,8 @@ public KgGraph call() throws Exception { this.kgGraphList = newKgGraphList; this.kgGraphSchema = KgGraphSchema.convert2KgGraphSchema(pattern); + java.util.Map debugInfoWithRule = + getStartIdWithHitRuleValue(this.kgGraphList, patternRuleLists); log.info( "LocalRDG patternScan,root=" @@ -327,7 +381,7 @@ public KgGraph call() throws Exception { "patternScan(" + RunnerUtil.getReadablePattern(pattern) + ")", this.kgGraphList.size(), "SubPattern", - generateRuntimeLog(failedStartIdSet, afterStartId), + generateRuntimeLog(failedStartIdSet, afterStartId, debugInfoWithRule), patternRuleLists); return this; } @@ -594,6 +648,9 @@ public LocalRDG expandInto(PatternElement target, Pattern pattern) { java.util.Set failedStartIdSet = getRemoveHashSet(originStartId, afterStartId); this.kgGraphSchema = afterKgGraphSchema; this.kgGraphList = newKgGraphList; + java.util.Map debugInfoWithRule = + getStartIdWithHitRuleValue(this.kgGraphList, patternRuleLists); + log.info( "LocalRDG ExpandInto,patternRoot=" + pattern.root() @@ -604,7 +661,7 @@ public LocalRDG expandInto(PatternElement target, Pattern pattern) { this.executionRecorder.stageResultWithDetail( "expandInto(" + RunnerUtil.getReadablePattern(pattern) + ")", this.kgGraphList.size(), - generateRuntimeLog(failedStartIdSet, afterStartId), + generateRuntimeLog(failedStartIdSet, afterStartId, debugInfoWithRule), patternRuleLists); return this; } @@ -676,6 +733,8 @@ public LocalRDG filter(Rule rule) { count += resultList.size(); newKgGraphList.addAll(resultList); } + java.util.Map debugInfoWithRule = + getStartIdWithHitRuleValue(this.kgGraphList, Lists.newArrayList(rule)); java.util.Set originStartId = getStartId(this.kgGraphList); java.util.Set afterStartId = getStartId(newKgGraphList); java.util.Set failedStartIdSet = getRemoveHashSet(originStartId, afterStartId); @@ -684,7 +743,7 @@ public LocalRDG filter(Rule rule) { this.executionRecorder.stageResultWithDetail( "filter(" + rule.getName() + "," + exprStringSet + ")", this.kgGraphList.size(), - generateRuntimeLog(failedStartIdSet, afterStartId), + generateRuntimeLog(failedStartIdSet, afterStartId, debugInfoWithRule), Lists.newArrayList(rule)); return this; } @@ -1079,7 +1138,7 @@ private java.util.Map getProcessInfo( DebugInfoWithStartId startId, String bizId, String targetLabel, String targetId) { java.util.Map processInfo = startId.toJsonObj(); if (StringUtils.isNotBlank(targetLabel)) { - processInfo.put("targetLabel", targetLabel.split("/")[0]); + processInfo.put("targetLabel", LabelTypeUtils.getMetaType(targetLabel)); processInfo.put("targetId", targetId); } java.util.Map startIdInfo = new HashMap<>(); @@ -1148,9 +1207,22 @@ private void addVertex(AddVertex addVertex) { long count = 0; for (KgGraph kgGraph : this.kgGraphList) { IVertex willAddedVertex = impl.extractVertex(kgGraph); - this.graphState.addVertex(willAddedVertex); + IVertex storedVertex = + this.graphState.getVertex(willAddedVertex.getId(), null); + if (storedVertex != null) { + java.util.Map property = new HashMap<>(); + for (String key : willAddedVertex.getValue().getKeySet()) { + property.put(key, willAddedVertex.getValue().get(key)); + } + this.graphState.mergeVertexProperty( + storedVertex.getId(), property, MergeTypeEnum.REPLACE, 0L); + } else { + this.graphState.addVertex(willAddedVertex); + } + storedVertex = this.graphState.getVertex(willAddedVertex.getId(), null); + // add to result list - this.resultVertexSet.add(willAddedVertex); + this.resultVertexSet.add(storedVertex); count++; } diff --git a/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/LocalRunnerTest.java b/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/LocalRunnerTest.java index 08de4375e..faedc5e40 100644 --- a/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/LocalRunnerTest.java +++ b/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/LocalRunnerTest.java @@ -15,8 +15,10 @@ import com.alibaba.fastjson.JSON; import com.antgroup.openspg.reasoner.common.constants.Constants; +import com.antgroup.openspg.reasoner.common.graph.edge.Direction; import com.antgroup.openspg.reasoner.common.utils.PropertyUtil; import com.antgroup.openspg.reasoner.lube.catalog.Catalog; +import com.antgroup.openspg.reasoner.lube.catalog.GeneralSemanticRule; import com.antgroup.openspg.reasoner.lube.catalog.impl.PropertyGraphCatalog; import com.antgroup.openspg.reasoner.progress.ProgressReport; import com.antgroup.openspg.reasoner.recorder.DefaultRecorder; @@ -37,6 +39,142 @@ import scala.Tuple2; public class LocalRunnerTest { + @Test + public void testCreateConceptInstance() { + String rule = + "Define (s:Test.User)-[p:belongTo]->(o:`Test.UserFeature`/`白领`) {\n" + + " GraphStructure {\n" + + " (s)\n" + + " }\n" + + " Rule {\n" + + " r1(\"属于教师\") = s.nightTrader == 1\n" + + " r2(\"属于程序员\") = s.nightTrader == 2\n" + + " r3(\"属于医生\") = s.nightTrader == 3\n" + + " s_r1 = rule_value(r1, \"教师\", \"\")\n" + + " s_r2 = rule_value(r2, \"程序员\", s_r1)\n" + + " s_r3 = rule_value(r3, \"医生\", s_r2)\n" + + " R: s_r3 != \"\"\n" + + " }\n" + + " Action {\n" + + " sub_concept = createNodeInstance(\n" + + " type=Test.UserFeature,\n" + + " value={\n" + + " id=concat(\"白领-\", s_r3)\n" + + " }\n" + + " )\n" + + " createEdgeInstance(\n" + + " src=s,\n" + + " dst=sub_concept,\n" + + " type=belongTo,\n" + + " value={\n" + + " __to_id_type__='Test.UserFeature'\n" + + " __from_id_type__='Test.User'\n" + + " }\n" + + " )\n" + + " }\n" + + "}"; + String rule1 = + "Define (s:Test.User)-[p:belongTo]->(o:`Test.UserFeature`/`学生`) {\n" + + " GraphStructure {\n" + + " (s)\n" + + " }\n" + + " Rule {\n" + + " r1(\"就读幼儿园\") = s.nightTrader == 1\n" + + " r2(\"就读小学\") = s.nightTrader == 2\n" + + " r3(\"就读中学\") = s.nightTrader == 3\n" + + " s_r1 = rule_value(r1, \"就读幼儿园\", \"\")\n" + + " s_r2 = rule_value(r2, \"就读小学\", s_r1)\n" + + " s_r3 = rule_value(r3, \"就读中学\", s_r2)\n" + + " R: s_r3 != \"\"\n" + + " }\n" + + " Action {\n" + + " sub_concept = createNodeInstance(\n" + + " type=Test.UserFeature,\n" + + " value={\n" + + " id=concat(\"学生-\", s_r3)\n" + + " }\n" + + " )\n" + + " createEdgeInstance(\n" + + " src=s,\n" + + " dst=sub_concept,\n" + + " type=belongTo,\n" + + " value={\n" + + " __to_id_type__='Test.UserFeature'\n" + + " __from_id_type__='Test.User'\n" + + " }\n" + + " )\n" + + " }\n" + + "}"; + String dsl = + "match (s:Test.User)-[p:belongTo]->(o:Test.UserFeature)-[p2:newedge]->(o2:Test.TaxOfUserFeature) return s.id,p,o.id, o2.id" + + ""; + // String dsl = rule; + LocalReasonerTask task = new LocalReasonerTask(); + task.setDsl(dsl); + task.setGraphLoadClass( + "com.antgroup.openspg.reasoner.runner.local.loader.TestFanxiqianGraphLoader"); + task.getParams().put(Constants.SPG_REASONER_PLAN_PRETTY_PRINT_LOGGER_ENABLE, true); + task.getParams().put(Constants.SPG_REASONER_LUBE_SUBQUERY_ENABLE, true); + task.getParams().put(ConfigKey.KG_REASONER_OUTPUT_GRAPH, true); + task.setStartIdList(Lists.newArrayList(new Tuple2<>("张三", "Test.User"))); + task.setExecutionRecorder(new DefaultRecorder()); + task.setExecutorTimeoutMs(99999999999999999L); + + // add mock catalog + Map> schema = new HashMap<>(); + schema.put( + "Test.User", + Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("id", "name", "nightTrader"))); + schema.put( + "Test.UserFeature", Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("id", "name"))); + schema.put( + "Test.TaxOfUserFeature", + Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("id", "name"))); + + schema.put( + "Test.User_belongTo_Test.UserFeature", + Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet())); + + schema.put( + "Test.UserFeature_newedge_Test.TaxOfUserFeature", + Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet())); + + Catalog catalog = new PropertyGraphCatalog(Convert2ScalaUtil.toScalaImmutableMap(schema)); + catalog.init(); + catalog + .getGraph("KG") + .registerRule("Test.User_belongTo_Test.UserFeature/白领", new GeneralSemanticRule(rule)); + catalog + .getGraph("KG") + .addEdge( + "Test.User", + "belongTo", + "Test.UserFeature/白领", + Direction.OUT, + Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet()), + false); + catalog + .getGraph("KG") + .registerRule("Test.User_belongTo_Test.UserFeature/学生", new GeneralSemanticRule(rule1)); + catalog + .getGraph("KG") + .addEdge( + "Test.User", + "belongTo", + "Test.UserFeature/学生", + Direction.OUT, + Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet()), + false); + task.setCatalog(catalog); + + LocalReasonerRunner runner = new LocalReasonerRunner(); + LocalReasonerResult result = runner.run(task); + System.out.println(result); + System.out.println(task.getExecutionRecorder().toReadableString()); + Assert.assertEquals(result.getRows().size(), 2); + Assert.assertEquals(result.getRows().get(0)[0], "张三"); + clear(); + } @Test public void doTestFilter() { @@ -470,111 +608,6 @@ public void doTestLocalRunnerDependency() { System.out.println(task.getExecutionRecorder().toReadableString()); } - @Test - public void testComplexMultiDefineDsl() { - String dsl = - "// 当月交易量\n" - + "Define (s:CustFundKG.Account)-[p:cur_month_num]->(o:Int) {\n" - + " GraphStructure {\n" - + " (u:CustFundKG.Account)-[t:accountFundContact]-(s)\n" - + " }\n" - + "\tRule {\n" - + " \tR1(\"当月交易量\"): date_diff(from_unix_time(now(), 'yyyyMMdd'),t.transDate) <= 20\n" - + " o = group(s).count(u.id)\n" - + " }\n" - + "}\n" - + "// 次月交易量\n" - + "Define (s:CustFundKG.Account)-[p:last_month_num]->(o:Int) {\n" - + " GraphStructure {\n" - + " (u:CustFundKG.Account)-[t:accountFundContact]-(s)\n" - + " }\n" - + "\tRule {\n" - + " \tdate_delta = date_diff(from_unix_time(now(), 'yyyyMMdd'),t.transDate)\n" - + " \tR1(\"次月交易量\"): date_delta > 20 && date_delta <=40\n" - + " o = group(s).count(u.id)\n" - + " }\n" - + "}\n" - + "\n" - + "// 次次月交易量\n" - + "Define (s:CustFundKG.Account)-[p:last_last_month_num]->(o:Int) {\n" - + " GraphStructure {\n" - + " (u:CustFundKG.Account)-[t:accountFundContact]-(s)\n" - + " }\n" - + "\tRule {\n" - + " \tdate_delta = date_diff(from_unix_time(now(), 'yyyyMMdd'),t.transDate)\n" - + " \tR1(\"次月交易量\"): date_delta > 40 && date_delta <=60\n" - + " o = group(s).count(u.id)\n" - + " }\n" - + "}\n" - + "// 倍数\n" - + "Define (s:CustFundKG.Account)-[p:last_trans_multiple]->(o:Float) {\n" - + "\t\tGraphStructure {\n" - + " (s)\n" - + " }\n" - + "\tRule {\n" - + " \tmultiple = s.last_month_num*1.0 / s.last_last_month_num\n" - + " o = multiple\n" - + " }\n" - + "}\n" - + "\n" - + "// 倍数\n" - + "Define (s:CustFundKG.Account)-[p:cur_trans_multiple]->(o:Float) {\n" - + "\t\tGraphStructure {\n" - + " (s)\n" - + " }\n" - + "\tRule {\n" - + " \tmultiple = s.cur_month_num*1.0 / s.last_month_num\n" - + " o = multiple\n" - + " }\n" - + "}\n" - + "\n" - + "Define (s:CustFundKG.Account)-[p:is_trans_raise_more_after_down]->(o:Boolean) {\n" - + "\t\tGraphStructure {\n" - + " (s)\n" - + " }\n" - + "\tRule {\n" - + " \tR1(\"T月交易量级超过T-1月交易量级3倍\"): s.last_trans_multiple >=3\n" - + " \tR2(\"T+1月交易量级小于T月交易量级的1/2\"): s.cur_trans_multiple <0.5\n" - + " \to = rule_value(R1 && R2, true, false)\n" - + " }\n" - + "}\n" - + "\n" - + "GraphStructure {\n" - + " (s:CustFundKG.Account)\n" - + "}\n" - + "Rule {\n" - + "}\n" - + "Action {\n" - + "\tget(s.id, s.is_trans_raise_more_after_down, s.cur_trans_multiple, s.last_trans_multiple, s" - + ".last_last_month_num, s.last_month_num, s.cur_month_num)\n" - + "}\n"; - LocalReasonerTask task = new LocalReasonerTask(); - task.setDsl(dsl); - task.setGraphLoadClass( - "com.antgroup.openspg.reasoner.runner.local.loader.TestMultiVersionGraphLoader"); - - // add mock catalog - Map> schema = new HashMap<>(); - schema.put("CustFundKG.Account", Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("id"))); - schema.put( - "CustFundKG.Account_accountFundContact_CustFundKG.Account", - Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("sumAmt", "transDate"))); - - Catalog catalog = new PropertyGraphCatalog(Convert2ScalaUtil.toScalaImmutableMap(schema)); - catalog.init(); - task.getParams().put(ConfigKey.KG_REASONER_CATALOG, SimpleObjSerde.ser(catalog)); - - task.setStartIdList(Lists.newArrayList(new Tuple2<>("1", "CustFundKG.Account"))); - - LocalReasonerRunner runner = new LocalReasonerRunner(); - LocalReasonerResult result = runner.run(task); - System.out.println("##########################"); - System.out.println(result); - System.out.println("##########################"); - - clear(); - } - @Test public void testEdgePropertyDefineDsl2() { String dsl = @@ -646,27 +679,29 @@ public void testEdgePropertyDefineDsl2() { @Test public void testEdgePropertyDefineDsl() { String dsl = - "//1 先定义每两个用户间进行聚合交易\n" + "//1 先定义每两个用户间进行聚合交易总值\n" + "Define (s:CustFundKG.Account)-[p:tranTargetUser]->(o:CustFundKG.Account) {\n" + " GraphStructure {\n" - + " (o)<-[t:accountFundContact]-(s)\n" + + " (o)-[t:accountFundContact]-(s)\n" + " }\n" + "\tRule {\n" + " tran_count = group(s,o).count(t.sumAmt)\n" + + " tran_amt = group(s,o).sum(t.sumAmt)\n" + " p.tran_count = tran_count\n" + + " p.tran_amt = tran_amt\n" + " }\n" + "}\n" + "\n" + "\n" + "\n" - + "Define (s:CustFundKG.Account)-[p:is_repeat_tran_user]->(o:Int) {\n" + + "//在定义经常交易的用户流水总和\n" + + "Define (s:CustFundKG.Account)-[p:total_trans_amt]->(o:Int) {\n" + " GraphStructure {\n" + " (s)-[t:tranTargetUser]->(u:CustFundKG.Account)\n" + " }\n" + "\tRule {\n" - + " user_num(\"交易笔数大于3的重复用户个数\") = group(s).countIf(t.tran_count>=3, u.id)\n" - + " R1(\"超过10个\"): user_num > 10\n" - + " o = rule_value(R1, true, false)\n" + + " R(\"交易笔数大于1才保留\") : t.tran_count>=1\n" + + " o = group(s).sum(t.tran_amt)\n" + " }\n" + "}\n" + "\n" @@ -677,7 +712,7 @@ public void testEdgePropertyDefineDsl() { + "Rule {\n" + "}\n" + "Action{\n" - + "\tget(s.id, s.is_repeat_tran_user)\n" + + "\tget(s.id, s.total_trans_amt)\n" + "}"; LocalReasonerTask task = new LocalReasonerTask(); task.setDsl(dsl); @@ -706,178 +741,16 @@ public void testEdgePropertyDefineDsl() { clear(); } - @Test - public void testComplexMultiTimeUDFDefineDsl() { - String dsl = - "//1 先定义流入资金总数\n" - + "Define (s:CustFundKG.Account)-[p:total_in_trans_num]->(o:Int) {\n" - + " GraphStructure {\n" - + " (u:CustFundKG.Account)-[t:accountFundContact]->(s)\n" - + " }\n" - + "\tRule {\n" - + " o = group(s).count(t.sumAmt)\n" - + " }\n" - + "}\n" - + "\n" - + "//2 定义整百流入笔数\n" - + "Define (s:CustFundKG.Account)-[p:multiples_hundred_in_trans_num]->(o:Int) {\n" - + " GraphStructure {\n" - + " (u:CustFundKG.Account)-[t:accountFundContact]->(s)\n" - + " }\n" - + "\tRule {\n" - + " \tR1(\"必须是整百交易\"): t.sumAmt % 100 == 0\n" - + " o = group(s).count(t.sumAmt)\n" - + " }\n" - + "}\n" - + "\n" - + "// 判断是否汇聚赌博\n" - + "Define (s:CustFundKG.Account)-[p:is_pooling_gambling_funds]->(o:Float) {\n" - + " GraphStructure {\n" - + " (s)\n" - + " }\n" - + "\tRule {\n" - + " R0(\"存在流入和整百资金\"): s.multiples_hundred_in_trans_num != null && s.total_in_trans_num != null\n" - + " \tR1(\"流入整百金额笔数大于2比\"): s.multiples_hundred_in_trans_num > 2\n" - + " R2(\"整百交易占比大于2%\"): s.multiples_hundred_in_trans_num /cast_type(s.total_in_trans_num,'double') > 0.02\n" - + " o = rule_value(R2, true, false)\n" - + " }\n" - + "}\n" - + "\n" - + "//获取结果\n" - + "GraphStructure {\n" - + "\ts [CustFundKG.Account]\n" - + "}\n" - + "Rule {\n" - + "}\n" - + "Action{\n" - + "\tget(s.id, s.is_pooling_gambling_funds, s.multiples_hundred_in_trans_num, s.total_in_trans_num)\n" - + "}"; - - LocalReasonerTask task = new LocalReasonerTask(); - task.setDsl(dsl); - task.setGraphLoadClass( - "com.antgroup.openspg.reasoner.runner.local.loader.TestMultiVersionGraphLoader"); - - // add mock catalog - Map> schema = new HashMap<>(); - schema.put("CustFundKG.Account", Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("id"))); - schema.put( - "CustFundKG.Account_accountFundContact_CustFundKG.Account", - Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("sumAmt"))); - - Catalog catalog = new PropertyGraphCatalog(Convert2ScalaUtil.toScalaImmutableMap(schema)); - catalog.init(); - task.getParams().put(ConfigKey.KG_REASONER_CATALOG, SimpleObjSerde.ser(catalog)); - - task.setStartIdList(Lists.newArrayList(new Tuple2<>("1", "CustFundKG.Account"))); - - LocalReasonerRunner runner = new LocalReasonerRunner(); - LocalReasonerResult result = runner.run(task); - System.out.println("##########################"); - System.out.println(result); - System.out.println("##########################"); - - clear(); - } - - @Test - public void doTestComplexDefineDsl() { - String dsl = - "// 1\n" - + "Define (s:DomainFamily)-[p:blackRelateRate]->(o:Pkg) {\n" - + " GraphStructure {\n" - + " (o)-[:use]->(d:Domain)-[:belong]->(s)\n" - + " }\n" - + " Rule {\n" - + " R1: o.is_black == true\n" - // + " domain_num = group(s,o).count(d)\n" - // + " p.same_domain_num = domain_num\n" - + " }\n" - + "}\n" - + "\n" - + "// 2\n" - + "Define (s:DomainFamily)-[p:total_domain_num]->(o:Int) {\n" - + " GraphStructure {\n" - + " (s)<-[:belong]-(d:Domain)\n" - + " }\n" - + " Rule {\n" - + " o = group(s).count(d.id)\n" - + " }\n" - + "}\n" - + "\n" - + "// 3\n" - + "Define (s:Pkg)-[p:target]->(o:User) {\n" - + " GraphStructure {\n" - + " (s)<-[p1:blackRelateRate]-(df:DomainFamily),\n" - + " (df)<-[:belong]-(d:Domain),\n" - + " (o)-[:visit]->(d)\n" - + " } Rule {\n" - + " visit_time = group(o, df).count(d.id)\n" - + " R1(\"必须大于1次\"): visit_time >= 1\n" - + " R2(\"必须占比大于50%\"): 1.0 * visit_time / df.total_domain_num > 0.2\n" - + " }\n" - + "}\n" - + "\n" - + "// 4\n" - + "GraphStructure {\n" - + " (s:Pkg)-[p:target]->(o:User)\n" - + "}\n" - + "Rule {\n" - + "\n" - + "}\n" - + "Action {\n" - + " get(s.id,o.id)\n" - + "}"; - - LocalReasonerTask task = new LocalReasonerTask(); - task.setDsl(dsl); - task.setGraphLoadClass( - "com.antgroup.openspg.reasoner.runner.local.loader.TestDefineGraphLoader"); - - // add mock catalog - Map> schema = new HashMap<>(); - schema.put( - "DomainFamily", - Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("id", "total_domain_num"))); - schema.put("Pkg", Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("id", "is_black"))); - schema.put("Domain", Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("id"))); - schema.put("User", Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("id"))); - schema.put("Pkg_use_Domain", Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet())); - schema.put( - "Domain_belong_DomainFamily", Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet())); - schema.put("User_visit_Domain", Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet())); - - // define - schema.put( - "DomainFamily_blackRelateRate_Pkg", - Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet())); - schema.put("Pkg_target_User", Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet())); - Catalog catalog = new PropertyGraphCatalog(Convert2ScalaUtil.toScalaImmutableMap(schema)); - catalog.init(); - task.getParams().put(ConfigKey.KG_REASONER_CATALOG, SimpleObjSerde.ser(catalog)); - - task.setStartIdList(Lists.newArrayList(new Tuple2<>("black_app_1", "Pkg"))); - task.setExecutionRecorder(new DefaultRecorder()); - - LocalReasonerRunner runner = new LocalReasonerRunner(); - LocalReasonerResult result = runner.run(task); - System.out.println("##########################"); - System.out.println(result); - System.out.println("##########################"); - System.out.println(task.getExecutionRecorder().toReadableString()); - clear(); - } - @Test public void doTestWithStart() { String dsl = - "// 查找使用了相同主演的两个导演\n" + "// ip在高危地区\n" + "GraphStructure {\n" + " (a:ABM.User)-[hasIp:acc2ip]->(ip:ABM.IP)\n" + "}\n" + "Rule {\n" + " \tR1(\"必选180天以内\"): hasIp.ipUse180dCnt >=2\n" - + " R2(\"必须是高危地区\"): ip.country in ['菲律宾','柬埔寨','老挝','日本','香港','台湾','泰国','澳门','越南','马来西亚','印度尼西亚']\n" + + " R2(\"必须是高危地区\"): ip.country in ['地址1','地址2','地址3']\n" + " result = rule_value(R2, true, false)\n" + "}\n" + "Action {\n" diff --git a/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/loader/TestFanxiqianGraphLoader.java b/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/loader/TestFanxiqianGraphLoader.java new file mode 100644 index 000000000..e0fc22061 --- /dev/null +++ b/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/loader/TestFanxiqianGraphLoader.java @@ -0,0 +1,43 @@ +/* + * 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.runner.local.loader; + +import com.antgroup.openspg.reasoner.common.graph.edge.IEdge; +import com.antgroup.openspg.reasoner.common.graph.property.IProperty; +import com.antgroup.openspg.reasoner.common.graph.vertex.IVertex; +import com.antgroup.openspg.reasoner.runner.local.load.graph.AbstractLocalGraphLoader; +import com.google.common.collect.Lists; +import java.util.List; + +public class TestFanxiqianGraphLoader extends AbstractLocalGraphLoader { + @Override + public List> genVertexList() { + return Lists.newArrayList( + constructionVertex("张三", "Test.User", "id", "张三", "name", "高级健康保险", "nightTrader", "3"), + constructionVertex( + "白领-医生", "Test.UserFeature", "id", "白领-医生", "name", "医生", "nightTrader", "3"), + constructionVertex( + "学生-就读中学", "Test.UserFeature", "id", "学生-就读中学", "name", "就读中学", "nightTrader", "3"), + constructionVertex( + "职业", "Test.TaxOfUserFeature", "id", "职业", "name", "职业", "nightTrader", "3"), + constructionVertex( + "学习阶段", "Test.TaxOfUserFeature", "id", "学习阶段", "name", "学习阶段", "nightTrader", "3")); + } + + @Override + public List> genEdgeList() { + return Lists.newArrayList( + constructionEdge("白领-医生", "newedge", "职业"), constructionEdge("学生-就读中学", "newedge", "学习阶段")); + } +} diff --git a/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/KgReasonerAddEdgeWithPropertyTest.java b/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/KgReasonerAddEdgeWithPropertyTest.java deleted file mode 100644 index 8127f2384..000000000 --- a/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/KgReasonerAddEdgeWithPropertyTest.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * 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.runner.local.main; - -import com.antgroup.openspg.reasoner.common.constants.Constants; -import com.antgroup.openspg.reasoner.common.graph.edge.IEdge; -import com.antgroup.openspg.reasoner.common.graph.property.IProperty; -import com.antgroup.openspg.reasoner.common.graph.vertex.IVertex; -import com.antgroup.openspg.reasoner.lube.catalog.Catalog; -import com.antgroup.openspg.reasoner.lube.catalog.impl.PropertyGraphCatalog; -import com.antgroup.openspg.reasoner.runner.local.LocalReasonerRunner; -import com.antgroup.openspg.reasoner.runner.local.load.graph.AbstractLocalGraphLoader; -import com.antgroup.openspg.reasoner.runner.local.model.LocalReasonerResult; -import com.antgroup.openspg.reasoner.runner.local.model.LocalReasonerTask; -import com.antgroup.openspg.reasoner.util.Convert2ScalaUtil; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.junit.Assert; -import org.junit.Test; - -public class KgReasonerAddEdgeWithPropertyTest { - @Test - public void testAddEdgeWithProperty() { - String dsl = - "Define (s:User)-[p:tradeInfo]->(o:User) {\n" - + " GraphStructure {\n" - + " (s:User)-[t:trade]->(o:User)\n" - + " } Rule {\n" - + " R1(\"交易时间在90天内\"): t.trade_time < now()\n" - + " trade_num(\"计算每个交易对象的交易次数\") = group(s,o).count(t)\n" - + " p.trade_num = trade_num\n" - + " }\n" - + "}\n" - + "\n" - + "Define (s:User)-[p:belongTo]->(o:CrowdType/`RepeatTradeUser`) {\n" - + " GraphStructure {\n" - + " (s:User)-[t:tradeInfo]->(u:User)\n" - + " } Rule {\n" - + " trade_user_count = group(s).count(u.id)\n" - + " R2(\"至少有3个交易对手\"): trade_user_count >= 3\n" - + " every_user_trade_more_then3 = group(s).countIf(t.trade_num > 3, t.trade_num)\n" - + " R3(\"每个交于对手交易大于3比\"): every_user_trade_more_then3 >= 1\n" - + " }\n" - + "}\n" - + "\n" - + "GraphStructure {\n" - + " (s:CrowdType/`RepeatTradeUser`)\n" - + "}\n" - + "Rule{\n" - + "}\n" - + "Action {\n" - + " get(s.id)\n" - + "}\n"; - - System.out.println(dsl); - LocalReasonerTask task = new LocalReasonerTask(); - task.setDsl(dsl); - - // add mock catalog - Map> schema = new HashMap<>(); - schema.put("User", Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("id"))); - schema.put("CrowdType", Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("id"))); - schema.put( - "User_trade_User", Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("trade_time"))); - Catalog catalog = new PropertyGraphCatalog(Convert2ScalaUtil.toScalaImmutableMap(schema)); - catalog.init(); - task.setCatalog(catalog); - - task.setGraphLoadClass( - "com.antgroup.openspg.reasoner.runner.local.main.KgReasonerAddEdgeWithPropertyTest$GraphLoader"); - - // enable subquery - Map params = new HashMap<>(); - params.put(Constants.SPG_REASONER_LUBE_SUBQUERY_ENABLE, true); - task.setParams(params); - - LocalReasonerRunner runner = new LocalReasonerRunner(); - LocalReasonerResult result = runner.run(task); - - // only u1 - Assert.assertEquals(1, result.getRows().size()); - Assert.assertEquals("u1", result.getRows().get(0)[0]); - } - - public static class GraphLoader extends AbstractLocalGraphLoader { - - @Override - public List> genVertexList() { - return Lists.newArrayList( - constructionVertex("u1", "User"), - constructionVertex("u2", "User"), - constructionVertex("u3", "User"), - constructionVertex("u4", "User"), - constructionVertex("RepeatTradeUser", "CrowdType")); - } - - @Override - public List> genEdgeList() { - return Lists.newArrayList( - constructionVersionEdge("u1", "trade", "u2", 1, "trade_time", 1), - constructionVersionEdge("u1", "trade", "u2", 2, "trade_time", 2), - constructionVersionEdge("u1", "trade", "u2", 3, "trade_time", 3), - constructionVersionEdge("u1", "trade", "u3", 1, "trade_time", 1), - constructionVersionEdge("u1", "trade", "u3", 2, "trade_time", 2), - constructionVersionEdge("u1", "trade", "u3", 3, "trade_time", 3), - constructionVersionEdge("u1", "trade", "u3", 4, "trade_time", 4), - constructionVersionEdge("u1", "trade", "u4", 1, "trade_time", 1), - constructionVersionEdge("u1", "trade", "u4", 2, "trade_time", 2), - constructionVersionEdge("u1", "trade", "u4", 3, "trade_time", 3), - constructionVersionEdge("u2", "trade", "u3", 1, "trade_time", 1), - constructionVersionEdge("u2", "trade", "u3", 2, "trade_time", 2), - constructionVersionEdge("u2", "trade", "u3", 3, "trade_time", 3), - constructionVersionEdge("u2", "trade", "u4", 1, "trade_time", 1), - constructionVersionEdge("u2", "trade", "u4", 2, "trade_time", 2), - constructionVersionEdge("u2", "trade", "u4", 3, "trade_time", 3)); - } - } -} diff --git a/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/KgReasonerZijinLocalTest.java b/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/KgReasonerZijinLocalTest.java index 176660052..32593b805 100644 --- a/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/KgReasonerZijinLocalTest.java +++ b/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/KgReasonerZijinLocalTest.java @@ -19,8 +19,6 @@ import com.antgroup.openspg.reasoner.common.graph.vertex.IVertex; import com.antgroup.openspg.reasoner.lube.catalog.Catalog; import com.antgroup.openspg.reasoner.lube.catalog.impl.PropertyGraphCatalog; -import com.antgroup.openspg.reasoner.recorder.DefaultRecorder; -import com.antgroup.openspg.reasoner.runner.ConfigKey; import com.antgroup.openspg.reasoner.runner.local.LocalReasonerRunner; import com.antgroup.openspg.reasoner.runner.local.load.graph.AbstractLocalGraphLoader; import com.antgroup.openspg.reasoner.runner.local.model.LocalReasonerResult; @@ -37,152 +35,6 @@ public class KgReasonerZijinLocalTest { - @Test - public void test41() { - String dsl = - "//异常借贷行为\n" - + "GraphStructure {\n" - + "A[AMLz50.BlackCompanyRelated]\n" - + "}\n" - + "Rule\n" - + "{\t\n" - + "R1(\"频繁花呗借款\") = rule_value(A.explainStrategyList rlike 'HuaBeiMultiLending', true, false)\n" - + "R2(\"花呗提前还款\") = rule_value(A.explainStrategyList rlike 'HuaBeiMultiPrepay', true, false)\n" - + "R3(\"花呗逾期未还\") = rule_value(A.explainStrategyList rlike 'HuaBeiCreditOverdue', true, false)\n" - + "\n" - + "result = R1 or R2 or R3\n" - + "\t\tr5 = rule_value(R3, \"花呗逾期未还\", \"\")\n" - + "\t\tr4 = rule_value(R1, \"频繁借款\", r5)\n" - + "\t\tr3 = rule_value(R1 && R3, \"频繁借款,花呗逾期未还\", r4)\n" - + " r2 = rule_value(R1 && R2, \"频繁借款后提前还款\", r3)\n" - + " r1 = rule_value(R1 && R2 && R3, \"频繁借款后提前还款,花呗逾期未还\", r2)\n" - + "}\n" - + "Action {\n" - + "\tget(A.id,result,r1)\n" - + "}"; - System.out.println(dsl); - LocalReasonerTask task = new LocalReasonerTask(); - task.setDsl(dsl); - // add mock catalog - Map> schema = new HashMap<>(); - schema.put( - "AMLz50.BlackCompanyRelated", - Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("explainStrategyList"))); - Catalog catalog = new PropertyGraphCatalog(Convert2ScalaUtil.toScalaImmutableMap(schema)); - catalog.init(); - task.setCatalog(catalog); - task.setGraphLoadClass( - "com.antgroup.openspg.reasoner.runner.local.main.KgReasonerZijinLocalTest$GraphLoader41"); - // enable subquery - Map params = new HashMap<>(); - params.put(ConfigKey.KG_REASONER_BINARY_PROPERTY, "false"); - params.put(Constants.SPG_REASONER_LUBE_SUBQUERY_ENABLE, true); - task.setParams(params); - task.setExecutorTimeoutMs(99999999999999999L); - task.setExecutionRecorder(new DefaultRecorder()); - LocalReasonerRunner runner = new LocalReasonerRunner(); - LocalReasonerResult result = runner.run(task); - System.out.println(task.getExecutionRecorder().toReadableString()); - // only u1 - Assert.assertEquals(1, result.getRows().size()); - } - - public static class GraphLoader41 extends AbstractLocalGraphLoader { - @Override - public List> genVertexList() { - return Lists.newArrayList(constructionVertex("SA", "AMLz50.BlackCompanyRelated")); - } - - @Override - public List> genEdgeList() { - return Lists.newArrayList(); - } - } - - @Test - public void test3() { - String dsl = - "// 当月交易量\n" - + "Define (s:CustFundKG.Account)-[p:cur_month_num]->(o:Int) {\n" - + " GraphStructure {\n" - + " (u:CustFundKG.Account)-[t:accountFundContact]-(s)\n" - + " }\n" - + "\tRule {\n" - + " \tR1(\"当月交易量\"): date_diff(from_unix_time(now(), 'yyyyMMdd'),t.transDate) <= 30\n" - + " o = group(s).count(t.transDate)\n" - + " }\n" - + "}\n" - + "// 次月交易量\n" - + "Define (s:CustFundKG.Account)-[p:last_month_num]->(o:Int) {\n" - + " GraphStructure {\n" - + " (u:CustFundKG.Account)-[t:accountFundContact]-(s)\n" - + " }\n" - + "\tRule {\n" - + " \tdate_delta = date_diff(from_unix_time(now(), 'yyyyMMdd'),t.transDate)\n" - + " \tR1(\"次月交易量\"): date_delta > 30 && date_delta <=60\n" - + " o = group(s).count(t.transDate)\n" - + " }\n" - + "}\n" - + "\n" - + "// 倍数\n" - + "Define (s:CustFundKG.Account)-[p:trans_multiple]->(o:Float) {\n" - + "\t\tGraphStructure {\n" - + " (s)\n" - + " }\n" - + "\tRule {\n" - + " \tcur_month = rule_value(s.cur_month_num==null, 0.0, s.cur_month_num*1.0)\n" - + " last_month = rule_value(s.last_month_num == null, 1, s.last_month_num)\n" - + " \tmultiple = cur_month / last_month\n" - + " o = multiple\n" - + " }\n" - + "}\n" - + "\n" - + "Define (s:CustFundKG.Account)-[p:is_trans_raise_more]->(o:Boolean) {\n" - + "\t\tGraphStructure {\n" - + " (s)\n" - + " }\n" - + "\tRule {\n" - + " \to = rule_value(s.trans_multiple >= 3, true, false)\n" - + " }\n" - + "}\n" - + "\n" - + "GraphStructure {\n" - + " (s:CustFundKG.Account)\n" - + "}\n" - + "Rule {\n" - + "}\n" - + "Action {\n" - + "\tget(s.id, s.is_trans_raise_more, s.trans_multiple, s.last_month_num, s.cur_month_num)\n" - + "}\n"; - LocalReasonerTask task = new LocalReasonerTask(); - task.setStartIdList(Lists.newArrayList(new Tuple2<>("2088xx3", "CustFundKG.Account"))); - - task.setExecutorTimeoutMs(60 * 1000 * 100); - task.setDsl(dsl); - - Map> schema = new HashMap<>(); - schema.put("CustFundKG.Account", Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("id"))); - schema.put( - "CustFundKG.Account_accountFundContact_CustFundKG.Account", - Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("transDate"))); - Catalog catalog = new PropertyGraphCatalog(Convert2ScalaUtil.toScalaImmutableMap(schema)); - catalog.init(); - task.setCatalog(catalog); - - task.setGraphLoadClass( - "com.antgroup.openspg.reasoner.runner.local.main.KgReasonerZijinLocalTest$GraphLoader"); - - // enable subquery - Map params = new HashMap<>(); - params.put(Constants.SPG_REASONER_LUBE_SUBQUERY_ENABLE, true); - task.setParams(params); - - LocalReasonerRunner runner = new LocalReasonerRunner(); - LocalReasonerResult result = runner.run(task); - System.out.println(result); - Assert.assertEquals(1, result.getRows().size()); - } - @Test public void test4() { String dsl = @@ -247,56 +99,4 @@ public List> genEdgeList() { constructionEdge("2088xx3", "accountFundContact", "2088xx4", "transDate", "20230801")); } } - - public static class GraphLoader2 extends AbstractLocalGraphLoader { - - @Override - public List> genVertexList() { - return Lists.newArrayList( - constructionVertex("day", "AttributePOC.TracebackDay"), - constructionVertex( - "s", "AttributePOC.BrinsonAttribute", "factorType", "total", "factorValue", 0.01), - constructionVertex( - "stock", - "AttributePOC.BrinsonAttribute", - "factorType", - "stock", - "factorValue", - 0.004), - constructionVertex( - "trade", - "AttributePOC.BrinsonAttribute", - "factorType", - "trade", - "factorValue", - -0.005), - constructionVertex( - "market", - "AttributePOC.BrinsonAttribute", - "factorType", - "market", - "factorValue", - 0.006), - constructionVertex( - "cluster", - "AttributePOC.BrinsonAttribute", - "factorType", - "cluster", - "factorValue", - -0.003), - constructionVertex("市场贡献大", "AttributePOC.TaxonomyOfBrinsonAttribute"), - constructionVertex("选股贡献大", "AttributePOC.TaxonomyOfBrinsonAttribute"), - constructionVertex("交易贡献大", "AttributePOC.TaxonomyOfBrinsonAttribute")); - } - - @Override - public List> genEdgeList() { - return Lists.newArrayList( - constructionEdge("day", "day", "s"), - constructionEdge("s", "factorValue", "stock"), - constructionEdge("s", "factorValue", "trade"), - constructionEdge("s", "factorValue", "market"), - constructionEdge("s", "factorValue", "cluster")); - } - } } diff --git a/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/group/concept/GroupConceptTest.java b/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/group/concept/GroupConceptTest.java index ef5116fa0..908c34757 100644 --- a/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/group/concept/GroupConceptTest.java +++ b/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/group/concept/GroupConceptTest.java @@ -58,7 +58,7 @@ public List> genVertexList() { constructionVertex("le2_2", "LoginEvent", "eventTime", "1640970059"), constructionVertex("le2_3", "LoginEvent", "eventTime", "1700353023"), constructionVertex("参加活动", "ActivityInfo"), - constructionVertex("参加活动/五福", "ActivityInfo"), + constructionVertex("参加活动/活动1", "ActivityInfo"), constructionVertex("参加活动/红包", "ActivityInfo"), constructionVertex("登录来源渠道", "LoginSource"), constructionVertex("主动登端", "LoginSource"), @@ -73,21 +73,21 @@ public List> genVertexList() { public List> genEdgeList() { return Lists.newArrayList( // hypernym - constructionEdge("参加活动/五福", "isA", "参加活动"), + constructionEdge("参加活动/活动1", "isA", "参加活动"), constructionEdge("参加活动/红包", "isA", "参加活动"), constructionEdge("主动登端", "isA", "登录来源渠道"), constructionEdge("渠道拉动登端", "isA", "登录来源渠道"), constructionEdge("主动登端-账户余额查询", "isA", "主动登端"), constructionEdge("e1_1", "subject", "u1"), - constructionEdge("e1_1", "activityName", "参加活动/五福"), + constructionEdge("e1_1", "activityName", "参加活动/活动1"), constructionEdge("e1_2", "subject", "u1"), - constructionEdge("e1_2", "activityName", "参加活动/五福"), + constructionEdge("e1_2", "activityName", "参加活动/活动1"), constructionEdge("e1_3", "subject", "u1"), constructionEdge("e1_3", "activityName", "参加活动/红包"), constructionEdge("e2_1", "subject", "u2"), - constructionEdge("e2_1", "activityName", "参加活动/五福"), + constructionEdge("e2_1", "activityName", "参加活动/活动1"), constructionEdge("e2_2", "subject", "u2"), - constructionEdge("e2_2", "activityName", "参加活动/五福"), + constructionEdge("e2_2", "activityName", "参加活动/活动1"), constructionEdge("le1_1", "subject", "u1"), constructionEdge("le1_1", "sourceClassification", "主动登端-账户余额查询"), constructionEdge("le1_2", "subject", "u1"), @@ -117,7 +117,7 @@ public void groupTest1() { LocalRunnerTestFactory.runTest( "Define (s:User) -[p:belongTo]-> (o:ActivityInfo) {\n" + " GraphStructure {\n" - + " (s) <-[:subject]- (e:InterviewEvent) -[:concept_edge_expand(e, 'activityName', ['参加活动/五福'], \n" + + " (s) <-[:subject]- (e:InterviewEvent) -[:concept_edge_expand(e, 'activityName', ['参加活动/活动1'], \n" + "'ActivityInfo')]-> (o)\n" + " }\n" + "Rule {\n" @@ -174,7 +174,7 @@ public void assertResult(LocalReasonerResult result) { } rstMap.put(key.toString(), String.valueOf(strings[5])); } - Assert.assertEquals("2", rstMap.get(",参加活动/五福,null,null,null,20220101")); + Assert.assertEquals("2", rstMap.get(",参加活动/活动1,null,null,null,20220101")); } }, this.params); diff --git a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/pattern/PatternMatcher.java b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/pattern/PatternMatcher.java index 7b85badfb..0566f738f 100644 --- a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/pattern/PatternMatcher.java +++ b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/pattern/PatternMatcher.java @@ -52,6 +52,7 @@ public class PatternMatcher implements Serializable { private final String taskId; private final GraphState graphState; private long initTime; + private boolean debugEnable = false; public PatternMatcher(String taskId, GraphState graphState) { this.taskId = taskId; @@ -63,6 +64,15 @@ public void resetInitTime() { this.initTime = System.currentTimeMillis(); } + /** + * set enable flag + * + * @param debugEnable + */ + public void setDebugEnable(boolean debugEnable) { + this.debugEnable = debugEnable; + } + /** * vertex center pattern match * @@ -92,6 +102,9 @@ public KgGraph patternMatch( } IVertex vertex = graphState.getVertex(id, endVersion, vertexRule); if (null == vertex) { + if (this.debugEnable) { + log.warn("PatternMatcher patternMatch get id=" + id.toString() + " is null"); + } return null; } @@ -100,7 +113,16 @@ public KgGraph patternMatch( // check root types PatternElement patternElement = pattern.root(); - if (!patternElement.typeNames().contains(RunnerUtil.getVertexType(vertex))) { + if (!patternElement.getMetaTypeNames().contains(RunnerUtil.getVertexType(vertex))) { + if (this.debugEnable) { + log.warn( + "PatternMatcher patternMatch meta type match failed id=" + + id.toString() + + " need=" + + patternElement.getMetaTypeNames() + + ", value is=" + + RunnerUtil.getVertexType(vertex)); + } return null; } @@ -108,7 +130,7 @@ public KgGraph patternMatch( Map vertexContext = RunnerUtil.vertexContext(vertex, pattern.root().alias()); if (!rootVertexRuleList.isEmpty() && !RuleRunner.getInstance().check(vertexContext, rootVertexRuleList, this.taskId)) { - if (DebugVertexIdSet.DEBUG_VERTEX_ID_SET.contains(vertex.getId())) { + if (DebugVertexIdSet.DEBUG_VERTEX_ID_SET.contains(vertex.getId()) || this.debugEnable) { log.info( "PatternMatch check vertex rule false, vertexContext=" + JSON.toJSONString(vertexContext) @@ -159,6 +181,19 @@ public KgGraph patternMatch( Sets.newHashSet(edgeType), direction, edgeTypeRuleMap); + if (this.debugEnable) { + log.warn( + "PatternMatcher patternMatch get edge id=" + + id.toString() + + " edgeType=" + + edgeType + + ", direction=" + + direction + + " edgeTypeRuleMap=" + + JSON.toJSONString(edgeTypeRuleMap) + + " edges=" + + JSON.toJSONString(edges)); + } edgeList.addAll(edges); } } @@ -187,6 +222,15 @@ public boolean test(IEdge edge) { return !validVertexIdSet.contains(edge.getTargetId()); } }); + if (this.debugEnable) { + log.warn( + "PatternMatcher patternMatch validVertexIdSet id=" + + id.toString() + + " willMatchEdgeList=" + + JSON.toJSONString(willMatchEdgeList) + + " validVertexIdSet=" + + JSON.toJSONString(validVertexIdSet)); + } } // check dst vertex rule list @@ -202,6 +246,15 @@ public boolean test(IEdge e) { return !RuleRunner.getInstance().check(context, dstVertexRuleList, taskId); } }); + if (this.debugEnable) { + log.warn( + "PatternMatcher patternMatch dstVertexRuleList id=" + + id.toString() + + " willMatchEdgeList=" + + JSON.toJSONString(willMatchEdgeList) + + " dstVertexRuleList=" + + JSON.toJSONString(dstVertexRuleList)); + } } List> validEdges = @@ -209,7 +262,7 @@ public boolean test(IEdge e) { vertexContext, willMatchEdgeList, patternConnection, pattern, edgeRuleMap, limit); if (CollectionUtils.isEmpty(validEdges)) { // one edge pattern connection no match - if (DebugVertexIdSet.DEBUG_VERTEX_ID_SET.contains(vertex.getId())) { + if (DebugVertexIdSet.DEBUG_VERTEX_ID_SET.contains(vertex.getId()) || this.debugEnable) { log.info( "PatternMatch edge not match, vertexContext=" + JSON.toJSONString(vertexContext) @@ -282,7 +335,7 @@ private boolean isEdgeMatch( // edge target type match if (!pattern .getNode(patternConnection.target()) - .typeNames() + .getMetaTypeNames() .contains(RunnerUtil.getEdgeTargetType(edge))) { return false; } @@ -290,7 +343,7 @@ private boolean isEdgeMatch( // edge source type match if (!pattern .getNode(patternConnection.source()) - .typeNames() + .getMetaTypeNames() .contains(RunnerUtil.getEdgeSourceType(edge))) { return false; } diff --git a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/DefaultRecorder.java b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/DefaultRecorder.java index 8ec7298ff..240d60dde 100644 --- a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/DefaultRecorder.java +++ b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/DefaultRecorder.java @@ -70,10 +70,7 @@ public void stageResultWithDesc(String stage, long result, String finishDescribe @Override public void stageResultWithDetail( - String stage, - long result, - Map> runtimeDetail, - List relateRules) { + String stage, long result, Map runtimeDetail, List relateRules) { SubAction nowAction = actionStack.peek(); nowAction.getSubActionList().add(new SampleAction(stage, result, runtimeDetail, relateRules)); } @@ -83,7 +80,7 @@ public void stageResultWithDescAndDetail( String stage, long result, String finishDescribe, - Map> runtimeDetail, + Map runtimeDetail, List relateRules) { SubAction nowAction = actionStack.peek(); nowAction diff --git a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/EmptyRecorder.java b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/EmptyRecorder.java index 72c40f5b0..af0fa33a8 100644 --- a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/EmptyRecorder.java +++ b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/EmptyRecorder.java @@ -45,16 +45,13 @@ public void stageResultWithDesc(String stage, long result, String finishDescribe @Override public void stageResultWithDetail( - String stage, - long result, - Map> runtimeDetail, - List relateRules) {} + String stage, long result, Map runtimeDetail, List relateRules) {} @Override public void stageResultWithDescAndDetail( String stage, long result, String finishDescribe, - Map> runtimeDetail, + Map runtimeDetail, List relateRules) {} } diff --git a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/IExecutionRecorder.java b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/IExecutionRecorder.java index b00558652..7c85d85b8 100644 --- a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/IExecutionRecorder.java +++ b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/IExecutionRecorder.java @@ -44,16 +44,13 @@ public interface IExecutionRecorder { /** record result num, like filer, expendInto */ void stageResultWithDetail( - String stage, - long result, - Map> runtimeDetail, - List relateRules); + String stage, long result, Map runtimeDetail, List relateRules); /** finish */ void stageResultWithDescAndDetail( String stage, long result, String finishDescribe, - Map> runtimeDetail, + Map runtimeDetail, List relateRules); } diff --git a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/DebugInfoWithRule.java b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/DebugInfoWithRule.java new file mode 100644 index 000000000..6c2a89f39 --- /dev/null +++ b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/DebugInfoWithRule.java @@ -0,0 +1,45 @@ +/* + * 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.recorder.action; + +import com.antgroup.openspg.reasoner.lube.common.rule.Rule; + +/** + * @author peilong.zpl + * @version $Id: DebugInfoWithRule.java, v 0.1 2024-06-06 13:37 peilong.zpl Exp $$ + */ +public class DebugInfoWithRule { + private Rule rule; + private String runtimeValue; + + public DebugInfoWithRule(Rule rule, String runtimeValue) { + this.rule = rule; + this.runtimeValue = runtimeValue; + } + + public Rule getRule() { + return rule; + } + + public void setRule(Rule rule) { + this.rule = rule; + } + + public String getRuntimeValue() { + return runtimeValue; + } + + public void setRuntimeValue(String runtimeValue) { + this.runtimeValue = runtimeValue; + } +} diff --git a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/DebugInfoWithStartId.java b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/DebugInfoWithStartId.java index e72fe6502..10dd5316b 100644 --- a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/DebugInfoWithStartId.java +++ b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/DebugInfoWithStartId.java @@ -13,7 +13,6 @@ package com.antgroup.openspg.reasoner.recorder.action; import com.antgroup.openspg.reasoner.common.graph.vertex.IVertexId; -import com.antgroup.openspg.reasoner.lube.common.rule.Rule; import com.antgroup.openspg.reasoner.warehouse.utils.WareHouseUtils; import java.util.ArrayList; import java.util.HashMap; @@ -27,8 +26,8 @@ */ public class DebugInfoWithStartId { private IVertexId vertexId; - private List hitRules = new ArrayList<>(); - private List failedRules = new ArrayList<>(); + private List hitRules = new ArrayList<>(); + private List failedRules = new ArrayList<>(); public IVertexId getVertexId() { return vertexId; @@ -38,20 +37,22 @@ public Map toJsonObj() { Map result = new HashMap<>(); List> hitRuleInfos = new ArrayList<>(); - for (Rule r : hitRules) { + for (DebugInfoWithRule r : hitRules) { Map hitRuleInfoDetail = new HashMap<>(); - hitRuleInfoDetail.put("ruleName", r.getName()); - hitRuleInfoDetail.put("ruleValue", StringUtils.join(WareHouseUtils.getRuleList(r), ",")); - hitRuleInfoDetail.put("hitValue", ""); + hitRuleInfoDetail.put("ruleName", r.getRule().getName()); + hitRuleInfoDetail.put( + "ruleValue", StringUtils.join(WareHouseUtils.getRuleList(r.getRule()), ",")); + hitRuleInfoDetail.put("hitValue", r.getRuntimeValue()); hitRuleInfos.add(hitRuleInfoDetail); } result.put("hitRule", hitRuleInfos); List> failedRuleInfos = new ArrayList<>(); - for (Rule r : failedRules) { + for (DebugInfoWithRule r : failedRules) { Map failedRuleInfoDetail = new HashMap<>(); - failedRuleInfoDetail.put("ruleName", r.getName()); - failedRuleInfoDetail.put("ruleValue", StringUtils.join(WareHouseUtils.getRuleList(r), ",")); - failedRuleInfoDetail.put("hitValue", ""); + failedRuleInfoDetail.put("ruleName", r.getRule().getName()); + failedRuleInfoDetail.put( + "ruleValue", StringUtils.join(WareHouseUtils.getRuleList(r.getRule()), ",")); + failedRuleInfoDetail.put("hitValue", r.getRuntimeValue()); failedRuleInfos.add(failedRuleInfoDetail); } result.put("failedRule", failedRuleInfos); @@ -70,19 +71,19 @@ public void setVertexId(IVertexId vertexId) { this.vertexId = vertexId; } - public List getHitRules() { + public List getHitRules() { return hitRules; } - public void setHitRules(List hitRules) { + public void setHitRules(List hitRules) { this.hitRules = hitRules; } - public List getFailedRules() { + public List getFailedRules() { return failedRules; } - public void setFailedRules(List failedRules) { + public void setFailedRules(List failedRules) { this.failedRules = failedRules; } } diff --git a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/SampleAction.java b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/SampleAction.java index 2d50d6b36..3fd113633 100644 --- a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/SampleAction.java +++ b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/SampleAction.java @@ -28,13 +28,14 @@ public class SampleAction extends AbstractAction { public static final String PASS_START_ID_KEY = "pass"; public static final String FAILED_START_ID_KEY = "failed"; + public static final String RULE_RUNTIME_VALUE = "rule_runtime_value"; private final List relateRules; private final String describe; private final Long num; private final String finishDescribe; - private final Map> runtimeDetail; + private final Map runtimeDetail; public SampleAction(String describe, long num) { super(System.currentTimeMillis()); @@ -46,10 +47,7 @@ public SampleAction(String describe, long num) { } public SampleAction( - String describe, - long num, - Map> runtimeDetail, - List relateRules) { + String describe, long num, Map runtimeDetail, List relateRules) { super(System.currentTimeMillis()); this.describe = describe; this.num = num; @@ -62,7 +60,7 @@ public SampleAction( String describe, long num, String finishDescribe, - Map> runtimeDetail, + Map runtimeDetail, List relateRules) { super(System.currentTimeMillis()); this.describe = describe; @@ -90,16 +88,31 @@ public SampleAction(String describe, Long num, long time) { this.relateRules = null; } + private String getRuntimeValue(IVertexId iVertexId) { + Map runtimeValueMap = getRuleRuntimeValue(); + return runtimeValueMap.computeIfAbsent(iVertexId, k -> ""); + } + + private List generateDebugInfoRuleSet(IVertexId iVertexId) { + String value = getRuntimeValue(iVertexId); + List res = new ArrayList<>(); + for (Rule rule : this.relateRules) { + res.add(new DebugInfoWithRule(rule, value)); + } + return res; + } + @Override public Map getRuleRuntimeInfo() { Map startIdMap = new HashMap<>(); if (this.runtimeDetail == null) { return startIdMap; } - for (IVertexId passId : getRuntimeDetail().get(PASS_START_ID_KEY)) { + + for (IVertexId passId : getPassStartID()) { DebugInfoWithStartId debugInfo = new DebugInfoWithStartId(); debugInfo.setVertexId(passId); - debugInfo.getHitRules().addAll(this.relateRules); + debugInfo.getHitRules().addAll(generateDebugInfoRuleSet(passId)); startIdMap.put(passId, debugInfo); } return startIdMap; @@ -121,10 +134,25 @@ public String getFinishDescribe() { return finishDescribe; } - public Map> getRuntimeDetail() { + public Map getRuntimeDetail() { return runtimeDetail; } + public List getPassStartID() { + return (List) + this.runtimeDetail.computeIfAbsent(PASS_START_ID_KEY, k -> new ArrayList<>()); + } + + public List getFailedStartID() { + return (List) + this.runtimeDetail.computeIfAbsent(FAILED_START_ID_KEY, k -> new ArrayList<>()); + } + + public Map getRuleRuntimeValue() { + return (Map) + this.runtimeDetail.computeIfAbsent(RULE_RUNTIME_VALUE, k -> new HashMap<>()); + } + @Override public String toString() { SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss.SSS"); @@ -133,15 +161,23 @@ public String toString() { StringBuilder failedIds = new StringBuilder(); if (runtimeDetail != null) { passIds.append(", [pass]{"); - for (IVertexId vertexId : - this.runtimeDetail.computeIfAbsent(PASS_START_ID_KEY, k -> new ArrayList<>())) { - passIds.append(" ").append(vertexId.toString()); + for (IVertexId vertexId : getPassStartID()) { + passIds + .append(" ") + .append(vertexId.toString()) + .append("(") + .append(getRuntimeValue(vertexId)) + .append(")"); } passIds.append("}"); failedIds.append(", [failed]{"); - for (IVertexId vertexId : - this.runtimeDetail.computeIfAbsent(FAILED_START_ID_KEY, k -> new ArrayList<>())) { - failedIds.append(" ").append(vertexId.toString()); + for (IVertexId vertexId : getFailedStartID()) { + failedIds + .append(" ") + .append(vertexId.toString()) + .append("(") + .append(getRuntimeValue(vertexId)) + .append(")"); } failedIds.append("}"); } diff --git a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/SubAction.java b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/SubAction.java index e30255ed5..fb86ec60e 100644 --- a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/SubAction.java +++ b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/SubAction.java @@ -34,6 +34,9 @@ public SubAction(String describe) { public Map getRuleRuntimeInfo() { Map vertexIdMap = new HashMap<>(); for (AbstractAction action : subActionList) { + if (!(action instanceof SampleAction)) { + continue; + } Map startIds = action.getRuleRuntimeInfo(); for (IVertexId d : startIds.keySet()) { if (vertexIdMap.containsKey(d)) { 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 cfe39d19e..d0e6d68a5 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 @@ -635,6 +635,9 @@ public static List loadCsvFile(String file) { /** get connection set from pattern */ public static Set getConnectionSet(Pattern schema) { Set connectionSet = new HashSet<>(); + if (schema.topology() == null || schema.topology().isEmpty()) { + return connectionSet; + } for (String pe : JavaConversions.mapAsJavaMap(schema.topology()).keySet()) { Set partConnectionSet = JavaConversions.setAsJavaSet(schema.topology().get(pe).get()); diff --git a/reasoner/runner/runner-common/src/main/scala/com/antgroup/openspg/reasoner/util/LoaderUtil.scala b/reasoner/runner/runner-common/src/main/scala/com/antgroup/openspg/reasoner/util/LoaderUtil.scala index 525ae2189..bf9aeba72 100644 --- a/reasoner/runner/runner-common/src/main/scala/com/antgroup/openspg/reasoner/util/LoaderUtil.scala +++ b/reasoner/runner/runner-common/src/main/scala/com/antgroup/openspg/reasoner/util/LoaderUtil.scala @@ -13,30 +13,22 @@ package com.antgroup.openspg.reasoner.util +import scala.collection.JavaConverters._ +import scala.collection.mutable + import com.antgroup.openspg.reasoner.common.constants.Constants import com.antgroup.openspg.reasoner.common.graph.edge.{Direction, SPO} import com.antgroup.openspg.reasoner.common.types.KTString +import com.antgroup.openspg.reasoner.common.utils.LabelTypeUtils import com.antgroup.openspg.reasoner.lube.catalog.{Catalog, SemanticPropertyGraph} import com.antgroup.openspg.reasoner.lube.catalog.struct.Field import com.antgroup.openspg.reasoner.lube.common.expr.VString -import com.antgroup.openspg.reasoner.lube.common.pattern.{ - EdgePattern, - LinkedPatternConnection, - PartialGraphPattern, - Pattern, - PatternConnection -} +import com.antgroup.openspg.reasoner.lube.common.pattern.{EdgePattern, LinkedPatternConnection, PartialGraphPattern, Pattern, PatternConnection} import com.antgroup.openspg.reasoner.lube.common.rule.Rule import com.antgroup.openspg.reasoner.lube.logical.{EdgeVar, NodeVar, SolvedModel, Var} import com.antgroup.openspg.reasoner.lube.logical.PatternOps.PatternOps import com.antgroup.openspg.reasoner.lube.logical.operators._ -import com.antgroup.openspg.reasoner.warehouse.common.config.{ - EdgeLoaderConfig, - GraphLoaderConfig, - VertexLoaderConfig -} -import scala.collection.JavaConverters._ -import scala.collection.mutable +import com.antgroup.openspg.reasoner.warehouse.common.config.{EdgeLoaderConfig, GraphLoaderConfig, VertexLoaderConfig} object LoaderUtil { @@ -89,7 +81,7 @@ object LoaderUtil { field match { case nodeVar: NodeVar => for (typeName <- solvedModel.getTypes(nodeVar.name)) { - val node = logicalPlan.graph.getNode(typeName.split("/")(0)) + val node = logicalPlan.graph.getNode(LabelTypeUtils.getMetaType(typeName)) if (node.resolved) { allVertexSet.add(node.typeName) } @@ -491,7 +483,7 @@ object LoaderUtil { field match { case nodeVar: NodeVar => for (typeName <- solvedModel.getTypes(field.name)) { - val node = logicalPlan.graph.getNode(typeName.split("/")(0)) + val node = logicalPlan.graph.getNode(LabelTypeUtils.getMetaType(typeName)) if (node.resolved) { val vertexLoaderConfig = new VertexLoaderConfig() if (typesAllowIsolateVertex.contains(node.typeName)) { @@ -520,7 +512,8 @@ object LoaderUtil { if (graph.containsEdge(typeName)) { val edge = logicalPlan.graph.getEdge(typeName) if (edge.resolved) { - val edgeTypeName = edge.startNode + "_" + edge.typeName + "_" + edge.endNode + val edgeTypeName = LabelTypeUtils.getMetaType(edge.startNode) + + "_" + edge.typeName + "_" + LabelTypeUtils.getMetaType(edge.endNode) val edgeLoaderConfig = new EdgeLoaderConfig() edgeLoaderConfig.setEdgeType(edgeTypeName) edgeLoaderConfig.setNeedProperties( 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 569d1f5c5..47cdce448 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 @@ -14,7 +14,9 @@ package com.antgroup.openspg.reasoner.warehouse.utils; import com.antgroup.openspg.reasoner.lube.common.expr.Expr; +import com.antgroup.openspg.reasoner.lube.common.graph.IREdge; import com.antgroup.openspg.reasoner.lube.common.graph.IRField; +import com.antgroup.openspg.reasoner.lube.common.graph.IRNode; import com.antgroup.openspg.reasoner.lube.common.pattern.Connection; import com.antgroup.openspg.reasoner.lube.common.pattern.Pattern; import com.antgroup.openspg.reasoner.lube.common.pattern.PatternElement; @@ -181,6 +183,29 @@ public static List getRuleList(Rule rule) { return Lists.newArrayList(JavaConversions.asJavaIterable(EXPR_TRANSFORMER.transform(rule))); } + /** get all ir filed * */ + public static List>> getRuleUsedAliasEle(Rule rule) { + List irFieldList = + JavaConversions.seqAsJavaList(RuleUtils.getAllInputFieldInRule(rule, null, null)); + List>> allFiledList = new ArrayList<>(); + for (IRField irField : irFieldList) { + if (irField instanceof IRNode) { + allFiledList.add( + new Tuple2>( + irField.name(), + Lists.newArrayList( + JavaConversions.seqAsJavaList(((IRNode) irField).fields().toSeq())))); + } else if (irField instanceof IREdge) { + allFiledList.add( + new Tuple2>( + irField.name(), + Lists.newArrayList( + JavaConversions.seqAsJavaList(((IREdge) irField).fields().toSeq())))); + } + } + return allFiledList; + } + /** transform rule to qlexpress */ public static Tuple2> getRuleListWithAlias(Rule rule) { List ruleList = From b56df31e2c971e66906edf3cf49166ceb58bb5bb Mon Sep 17 00:00:00 2001 From: wenchengyao <45688588+ChengyaoWen@users.noreply.github.com> Date: Mon, 1 Jul 2024 12:06:23 +0800 Subject: [PATCH 14/29] fix(reasoner): fix bugs in cast_type (#276) --- .../com/antgroup/openspg/reasoner/udf/builtin/udf/Cast.java | 3 ++- .../java/com/antgroup/openspg/reasoner/udf/test/UdfTest.java | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/builtin/udf/Cast.java b/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/builtin/udf/Cast.java index 65fe32965..ad0a9937b 100644 --- a/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/builtin/udf/Cast.java +++ b/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/builtin/udf/Cast.java @@ -14,6 +14,7 @@ package com.antgroup.openspg.reasoner.udf.builtin.udf; import com.antgroup.openspg.reasoner.udf.model.UdfDefine; +import java.math.BigDecimal; public class Cast { @UdfDefine(name = "cast_type", compatibleName = "Cast") @@ -25,7 +26,7 @@ public Object cast(Object op1, Object targetType) { if ("long".equalsIgnoreCase(castType) || "bigint".equalsIgnoreCase(castType) || "int".equalsIgnoreCase(castType)) { - return Long.valueOf(opdata1); + return Long.valueOf(new BigDecimal(opdata1).longValueExact()); } else if ("double".equalsIgnoreCase(castType) || "float".equalsIgnoreCase(castType)) { return Double.valueOf(opdata1); } else { diff --git a/reasoner/udf/src/test/java/com/antgroup/openspg/reasoner/udf/test/UdfTest.java b/reasoner/udf/src/test/java/com/antgroup/openspg/reasoner/udf/test/UdfTest.java index c6112af3d..d47cafccf 100644 --- a/reasoner/udf/src/test/java/com/antgroup/openspg/reasoner/udf/test/UdfTest.java +++ b/reasoner/udf/src/test/java/com/antgroup/openspg/reasoner/udf/test/UdfTest.java @@ -183,6 +183,10 @@ public void testCast() { Assert.assertEquals(rst, 60L); rst = udfMeta.invoke("60.1", "float"); Assert.assertEquals(rst, 60.1); + rst = udfMeta.invoke("3.3e7", "long"); + Assert.assertEquals(rst, 33000000L); + rst = udfMeta.invoke("1.234E2", "double"); + Assert.assertEquals(rst, 123.4); try { udfMeta.invoke("abc", "float"); Assert.assertTrue(false); From a2d3fc0eaf3673597a4be3f51e796da9fb096f38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=9F=B9=E9=BE=99?= Date: Mon, 1 Jul 2024 14:17:35 +0800 Subject: [PATCH 15/29] fix(reasoner): fix bugs for edge limit (#301) --- .../local/main/KgReasonerTopKFilmTest.java | 41 +++++++++++++++++++ .../reasoner/pattern/PatternMatcher.java | 4 +- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/KgReasonerTopKFilmTest.java b/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/KgReasonerTopKFilmTest.java index 1572df587..2ba74a0b3 100644 --- a/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/KgReasonerTopKFilmTest.java +++ b/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/KgReasonerTopKFilmTest.java @@ -416,4 +416,45 @@ private void doTest12() { Assert.assertEquals("f1", result.get(0)[1]); Assert.assertEquals("700", result.get(0)[2]); } + + @Test + public void test13() { + FileMutex.runTestWithMutex(this::doTest13); + } + + private void doTest13() { + String dsl = + "match (s:Film)-[f:starOfFilm PER_NODE_LIMIT 1]->(star:FilmStar) where s.id = 'root' return s.id, star.id"; + List result = runTestResult(dsl); + Assert.assertEquals(1, result.size()); + Assert.assertEquals(2, result.get(0).length); + Assert.assertEquals("root", result.get(0)[0]); + Assert.assertEquals("L1_1_star", result.get(0)[1]); + } + + @Test + public void test14() { + FileMutex.runTestWithMutex(this::doTest14); + } + + private void doTest14() { + String dsl = + "\n" + + "GraphStructure {\n" + + " s [Film, __start__='true']\n" + + " star [FilmStar]\n" + + " s->star [starOfFilm, PRE_NODE_LIMIT=1] as sf \n" + + "}\n" + + "Rule {\n" + + "R1: s.id=='root'\n" + + "}\n" + + "Action {\n" + + " get(s.id, star.id)\n" + + "}"; + List result = runTestResult(dsl); + Assert.assertEquals(1, result.size()); + Assert.assertEquals(2, result.get(0).length); + Assert.assertEquals("root", result.get(0)[0]); + Assert.assertEquals("L1_1_star", result.get(0)[1]); + } } diff --git a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/pattern/PatternMatcher.java b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/pattern/PatternMatcher.java index 0566f738f..06c3bdd9c 100644 --- a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/pattern/PatternMatcher.java +++ b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/pattern/PatternMatcher.java @@ -256,7 +256,9 @@ public boolean test(IEdge e) { + JSON.toJSONString(dstVertexRuleList)); } } - + if (patternConnection.limit() != null && patternConnection.limit() > 0) { + limit = new Long(patternConnection.limit()); + } List> validEdges = matchEdges( vertexContext, willMatchEdgeList, patternConnection, pattern, edgeRuleMap, limit); From f71ee6acd54294c6252309b9ff13ccb773f10cfe Mon Sep 17 00:00:00 2001 From: wenchengyao <45688588+ChengyaoWen@users.noreply.github.com> Date: Tue, 9 Jul 2024 10:30:53 +0800 Subject: [PATCH 16/29] fix(reasoner): fix udf json_get (#317) --- .../udf/builtin/udf/JsonStringGet.java | 38 +++++++++++-------- .../openspg/reasoner/udf/test/UdfTest.java | 35 ++++++++--------- 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/builtin/udf/JsonStringGet.java b/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/builtin/udf/JsonStringGet.java index 8f6ea322d..35445ca03 100644 --- a/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/builtin/udf/JsonStringGet.java +++ b/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/builtin/udf/JsonStringGet.java @@ -14,9 +14,8 @@ package com.antgroup.openspg.reasoner.udf.builtin.udf; import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONPath; +import com.alibaba.fastjson.JSONValidator; import com.antgroup.openspg.reasoner.udf.model.UdfDefine; import java.util.ArrayList; import java.util.List; @@ -24,6 +23,21 @@ public class JsonStringGet { + /** + * @param jsonString + * @return + */ + private static Object parseJson(String jsonString) { + JSONValidator validator = JSONValidator.from(jsonString); + JSONValidator.Type type = validator.getType(); + if (type == JSONValidator.Type.Object) { + return JSON.parseObject(jsonString); + } else if (type == JSONValidator.Type.Array) { + return JSON.parseArray(jsonString); + } + return null; + } + /** * @param plainJson * @param jsonPath @@ -32,26 +46,18 @@ public class JsonStringGet { @UdfDefine(name = "json_get", compatibleName = "UDF_JsonGet") public Object jsonStrGet(String plainJson, String jsonPath) { try { - JSONObject jsonObject = JSON.parseObject(plainJson); + if (plainJson == null) { + return ""; + } + Object jsonObject = parseJson(plainJson); if (jsonObject != null) { Object result = JSONPath.eval(jsonObject, jsonPath); if (result != null) { return result; } } - } catch (Exception e1) { - try { - JSONArray jsonArray = JSON.parseArray(plainJson); - for (Object item : jsonArray) { - JSONObject jsonObject = (JSONObject) item; - Object result = JSONPath.eval(jsonObject, jsonPath); - if (result != null) { - return result; - } - } - } catch (Exception e2) { - return ""; - } + } catch (Exception e) { + return ""; } return ""; } diff --git a/reasoner/udf/src/test/java/com/antgroup/openspg/reasoner/udf/test/UdfTest.java b/reasoner/udf/src/test/java/com/antgroup/openspg/reasoner/udf/test/UdfTest.java index d47cafccf..82d0cb248 100644 --- a/reasoner/udf/src/test/java/com/antgroup/openspg/reasoner/udf/test/UdfTest.java +++ b/reasoner/udf/src/test/java/com/antgroup/openspg/reasoner/udf/test/UdfTest.java @@ -13,6 +13,7 @@ package com.antgroup.openspg.reasoner.udf.test; +import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.antgroup.openspg.reasoner.common.types.KTArray; import com.antgroup.openspg.reasoner.common.types.KTBoolean$; @@ -67,51 +68,45 @@ public void testReMatch() { @Test public void testJsonGet() { UdfMng mng = UdfMngFactory.getUdfMng(); - String params = "{'v':'123'}"; + String params = "{\"v\":\"123\"}"; IUdfMeta udfMeta = mng.getUdfMeta("json_get", Lists.newArrayList(KTString$.MODULE$, KTString$.MODULE$)); Object rst = udfMeta.invoke(params, "$.v"); - Assert.assertEquals(rst, "123"); - } - - @Test - public void testJsonGet1() { - UdfMng mng = UdfMngFactory.getUdfMng(); - String params = "[{'v':'123'}]"; - IUdfMeta udfMeta = - mng.getUdfMeta("json_get", Lists.newArrayList(KTString$.MODULE$, KTString$.MODULE$)); - Object rst = udfMeta.invoke(params, "$.v"); - Assert.assertEquals(rst, "123"); + Assert.assertEquals("123", rst); } @Test public void testJsonGet2() { UdfMng mng = UdfMngFactory.getUdfMng(); - String params = "[{'v':'123'}, {'k':'456'}]"; + String params = "[{\"v\":\"123\"}, {\"k\":\"456\"}]"; IUdfMeta udfMeta = mng.getUdfMeta("json_get", Lists.newArrayList(KTString$.MODULE$, KTString$.MODULE$)); Object rst = udfMeta.invoke(params, "$.k"); - Assert.assertEquals(rst, "456"); + Assert.assertEquals(JSONArray.parse("[\"456\"]"), rst); } @Test public void testJsonGet3() { UdfMng mng = UdfMngFactory.getUdfMng(); - String params = "[{'v': {'v1': '111', 'v2': '222'}}, {'k': {'k1': '333', 'k2': '444'}}]"; + String params = + "[{\"v\": {\"v1\": \"111\", \"v2\": \"222\"}}, {\"k\": {\"k1\": \"333\", \"k2\": \"444\"}}]"; IUdfMeta udfMeta = mng.getUdfMeta("json_get", Lists.newArrayList(KTString$.MODULE$, KTString$.MODULE$)); Object rst = udfMeta.invoke(params, "$.k.k2"); - Assert.assertEquals(rst, "444"); + Assert.assertEquals(JSONArray.parse("[\"444\"]"), rst); } @Test public void testJsonGet4() { UdfMng mng = UdfMngFactory.getUdfMng(); - String params = "[{'案由': '打架斗殴', '日期': '20240101'}, {'案由': '制造毒品', '日期': '20240202'}]"; + String params = + "[{\"案由\": \"打架斗殴\", \"日期\": \"20240101\"}, {\"案由\": \"制造毒品\", \"日期\": \"20240202\"}]"; IUdfMeta udfMeta = mng.getUdfMeta("json_get", Lists.newArrayList(KTString$.MODULE$, KTString$.MODULE$)); Object rst = udfMeta.invoke(params, "$[案由 rlike '(.*)毒品(.*)'].案由"); - Assert.assertEquals(rst, "制造毒品"); + Assert.assertEquals(JSONArray.parse("[\"制造毒品\"]"), rst); + rst = udfMeta.invoke(params, null); + Assert.assertEquals("", rst); } @Test @@ -126,12 +121,12 @@ public void testRdfProperty() { + "}"; ; Map paramsMap = JSONObject.parseObject(params, Map.class); - paramsMap.put("basicInfo", "{'v':'123'}"); + paramsMap.put("basicInfo", "{\"v\":\"123\"}"); IUdfMeta udfMeta = mng.getUdfMeta( "get_rdf_property", Lists.newArrayList(KTObject$.MODULE$, KTString$.MODULE$)); Object rst = udfMeta.invoke(paramsMap, "v"); - Assert.assertEquals(rst, "123"); + Assert.assertEquals("123", rst); } @Test From e1d814cf0d881a01b2b5321c8187b950913a022c Mon Sep 17 00:00:00 2001 From: FishJoy Date: Fri, 12 Jul 2024 16:52:48 +0800 Subject: [PATCH 17/29] fix(reasoner): fix unit test in hypertension (#321) --- .../reasoner/common/constants/Constants.java | 3 + .../thinker/engine/DefaultThinker.java | 4 +- .../reasoner/thinker/engine/GraphStore.java | 9 ++- .../reasoner/thinker/engine/InfGraph.java | 33 +++++++++-- .../logic/graph/CombinationEntity.java | 29 ++++++++++ .../reasoner/thinker/logic/graph/Element.java | 4 ++ .../reasoner/thinker/logic/graph/Entity.java | 5 ++ .../reasoner/thinker/logic/graph/Node.java | 3 + .../thinker/logic/graph/Predicate.java | 5 ++ .../reasoner/thinker/logic/graph/Triple.java | 7 +++ .../logic/rule/exact/QlExpressCondition.java | 3 +- .../logic/rule/visitor/RuleExecutor.java | 33 +++++++++-- .../openspg/reasoner/thinker/GraphUtil.java | 9 ++- .../openspg/reasoner/thinker/MedTests.java | 58 ++++++++++++++----- .../src/test/resources/Hypertension.txt | 54 ++++++++++------- .../thinker/src/test/resources/Medical.txt | 16 +++++ 16 files changed, 224 insertions(+), 51 deletions(-) diff --git a/reasoner/common/src/main/java/com/antgroup/openspg/reasoner/common/constants/Constants.java b/reasoner/common/src/main/java/com/antgroup/openspg/reasoner/common/constants/Constants.java index 0cb6f45ee..c17372b7f 100644 --- a/reasoner/common/src/main/java/com/antgroup/openspg/reasoner/common/constants/Constants.java +++ b/reasoner/common/src/main/java/com/antgroup/openspg/reasoner/common/constants/Constants.java @@ -55,6 +55,9 @@ public class Constants { public static final String SPG_REASONER_PLAN_PRETTY_PRINT_LOGGER_ENABLE = "spg.reasoner.plan.pretty.print.logger.enable"; + /** enable strict mode in thinker during rule engine execution, default is false */ + public static final String SPG_REASONER_THINKER_STRICT = "spg.reasoner.thinker.strict"; + /** start label config */ public static final String START_LABEL = "start_label"; diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/DefaultThinker.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/DefaultThinker.java index 1954164f3..ebcb4522e 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/DefaultThinker.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/DefaultThinker.java @@ -46,13 +46,13 @@ public List find(Element s, Element p, Element o) { public List find(Element s, Element p, Element o, Map context) { this.infGraph.clear(); Triple pattern = Triple.create(s, p, o); - List result = this.infGraph.find(pattern, context); + List result = this.infGraph.find(pattern, context == null ? new HashMap<>() : context); return result; } @Override public List find(Node s, Map context) { this.infGraph.clear(); - return this.infGraph.find(s, context); + return this.infGraph.find(s, context == null ? new HashMap<>() : context); } } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/GraphStore.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/GraphStore.java index a0b6a6945..8c0160aa8 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/GraphStore.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/GraphStore.java @@ -16,6 +16,7 @@ import com.antgroup.openspg.reasoner.common.constants.Constants; import com.antgroup.openspg.reasoner.common.graph.edge.Direction; import com.antgroup.openspg.reasoner.common.graph.edge.IEdge; +import com.antgroup.openspg.reasoner.common.graph.edge.SPO; import com.antgroup.openspg.reasoner.common.graph.property.IProperty; import com.antgroup.openspg.reasoner.common.graph.vertex.IVertex; import com.antgroup.openspg.reasoner.common.graph.vertex.IVertexId; @@ -85,8 +86,9 @@ protected List getTriple(Triple triple, Predicate predicate) { this.graphState.getEdges( IVertexId.from(s.getId(), s.getType()), null, null, null, Direction.OUT); for (IEdge edge : edges) { + SPO spo = new SPO(edge.getType()); if (edge.getTargetId().equals(IVertexId.from(o.getId(), o.getType())) - && edge.getType().equals(p.getName())) { + && spo.getP().equals(p.getName())) { triples.add( new Triple(triple, predicate, new Value(edge.getValue().get(predicate.getName())))); } @@ -95,12 +97,13 @@ protected List getTriple(Triple triple, Predicate predicate) { } private Triple edgeToTriple(IEdge edge) { + SPO spo = new SPO(edge.getType()); if (edge.getDirection() == Direction.OUT) { return new Triple( new Entity( (String) edge.getValue().get(Constants.EDGE_FROM_ID_KEY), (String) edge.getValue().get(Constants.EDGE_FROM_ID_TYPE_KEY)), - new Predicate(edge.getType()), + new Predicate(spo.getP()), new Entity( (String) edge.getValue().get(Constants.EDGE_TO_ID_KEY), (String) edge.getValue().get(Constants.EDGE_TO_ID_TYPE_KEY))); @@ -109,7 +112,7 @@ private Triple edgeToTriple(IEdge edge) { new Entity( (String) edge.getValue().get(Constants.EDGE_FROM_ID_KEY), (String) edge.getValue().get(Constants.EDGE_FROM_ID_TYPE_KEY)), - new Predicate(edge.getType()), + new Predicate(spo.getP()), new Entity( (String) edge.getValue().get(Constants.EDGE_TO_ID_KEY), (String) edge.getValue().get(Constants.EDGE_TO_ID_TYPE_KEY))); diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/InfGraph.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/InfGraph.java index e67a40ec6..71a1d2582 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/InfGraph.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/InfGraph.java @@ -13,6 +13,7 @@ package com.antgroup.openspg.reasoner.thinker.engine; +import com.antgroup.openspg.reasoner.common.constants.Constants; import com.antgroup.openspg.reasoner.thinker.TripleStore; import com.antgroup.openspg.reasoner.thinker.logic.LogicNetwork; import com.antgroup.openspg.reasoner.thinker.logic.Result; @@ -93,6 +94,8 @@ private void prepareContext(Map context) { private List inference(Element pattern, Map context) { List rst = new LinkedList<>(); + boolean strictMode = + (Boolean) context.getOrDefault(Constants.SPG_REASONER_THINKER_STRICT, false); for (Rule rule : logicNetwork.getBackwardRules(pattern)) { List body = rule.getBody().stream().map(ClauseEntry::toElement).collect(Collectors.toList()); @@ -100,7 +103,8 @@ private List inference(Element pattern, Map context) { if (CollectionUtils.isEmpty(data)) { TreeLogger traceLogger = new TreeLogger(rule.getRoot().toString()); Boolean ret = - rule.getRoot().accept(new LinkedList<>(), context, new RuleExecutor(), traceLogger); + rule.getRoot() + .accept(new LinkedList<>(), context, new RuleExecutor(strictMode), traceLogger); traceLogger.setCurrentNodeMsg(rule.getDesc()); if (ret) { Element ele = rule.getHead().toElement(); @@ -115,7 +119,8 @@ private List inference(Element pattern, Map context) { for (List d : data) { TreeLogger traceLogger = new TreeLogger(rule.getRoot().toString()); List dList = d.stream().map(Result::getData).collect(Collectors.toList()); - Boolean ret = rule.getRoot().accept(dList, context, new RuleExecutor(), traceLogger); + Boolean ret = + rule.getRoot().accept(dList, context, new RuleExecutor(strictMode), traceLogger); List msg = d.stream() .map(Result::getTraceLog) @@ -193,6 +198,22 @@ private List> prepareElements( evidence.addAll(prepareElement(e, context)); } } + } else if (!e.canInstantiated()) { + List> singeRst = prepareElement(null, (Triple) e, context); + if (CollectionUtils.isEmpty(elements)) { + elements.addAll(singeRst); + } else { + List> tmpElements = new LinkedList<>(); + for (List ele : elements) { + for (List single : singeRst) { + List cp = new LinkedList<>(ele); + cp.addAll(single); + tmpElements.add(cp); + } + } + elements.clear(); + elements = tmpElements; + } } else { Triple t = (Triple) e; if (t.getSubject().alias().equals(s.alias())) { @@ -213,9 +234,11 @@ private List> prepareElements( choose.add(e); if (CollectionUtils.isEmpty(elements)) { Triple triple = buildTriple(null, s, t); - List> singeRst = prepareElement(null, triple, context); - if (CollectionUtils.isNotEmpty(singeRst)) { - elements.addAll(singeRst); + if (triple != null) { + List> singeRst = prepareElement(null, triple, context); + if (CollectionUtils.isNotEmpty(singeRst)) { + elements.addAll(singeRst); + } } } else { List> tmpElements = new LinkedList<>(); diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/CombinationEntity.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/CombinationEntity.java index 08a7d560c..1fb24d48e 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/CombinationEntity.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/CombinationEntity.java @@ -16,6 +16,8 @@ import com.alibaba.fastjson.JSON; import java.util.List; import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; public class CombinationEntity extends Element { private List entityList; @@ -32,6 +34,33 @@ public CombinationEntity(List entityList, String alias) { this.alias = alias; } + @Override + public boolean canInstantiated() { + return false; + } + + public Set types() { + return entityList.stream().map(Entity::getType).collect(Collectors.toSet()); + } + + public boolean matches(Element other) { + if (other == null) { + return false; + } + if (other instanceof Node) { + return types().contains(((Node) other).getType()); + } + if (other instanceof Entity) { + return entityList.contains(other); + } + return equals(other); + } + + @Override + public String alias() { + return this.alias; + } + /** * Getter method for property entityList. * diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Element.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Element.java index d5cd0024d..1aff3fbd2 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Element.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Element.java @@ -29,6 +29,10 @@ public Element bind(Element pattern) { return this; } + public boolean canInstantiated() { + return true; + } + public String alias() { throw new UnsupportedOperationException( this.getClass().getSimpleName() + " cannot support", null); diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Entity.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Entity.java index d17461f1c..05212ad53 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Entity.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Entity.java @@ -121,6 +121,11 @@ public String alias() { return this.alias; } + @Override + public boolean canInstantiated() { + return false; + } + @Override public Element cleanAlias() { return new Entity(this.id, this.type); diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Node.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Node.java index 9807fcfe3..9d75fce30 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Node.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Node.java @@ -68,6 +68,9 @@ public boolean matches(Element other) { public Element bind(Element pattern) { if (pattern instanceof Entity) { return new Entity(((Entity) pattern).getId(), this.type, ((Entity) pattern).getAlias()); + } else if (pattern instanceof CombinationEntity) { + Entity entity = ((CombinationEntity) pattern).getEntityList().get(0); + return new Entity(entity.getId(), entity.getType(), entity.getAlias()); } else { return this; } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Predicate.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Predicate.java index c4edc5373..7129b26e4 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Predicate.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Predicate.java @@ -96,6 +96,11 @@ public boolean matches(Element other) { return equals(other); } + @Override + public boolean canInstantiated() { + return false; + } + public String alias() { return alias; } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Triple.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Triple.java index 61b0e62b5..b0e1f27d9 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Triple.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Triple.java @@ -53,6 +53,13 @@ public Element bind(Element pattern) { } } + @Override + public boolean canInstantiated() { + return !(!subject.canInstantiated() + && !predicate.canInstantiated() + && !object.canInstantiated()); + } + public String alias() { return predicate.alias(); } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/exact/QlExpressCondition.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/exact/QlExpressCondition.java index 7590b1a12..012a850a2 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/exact/QlExpressCondition.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/exact/QlExpressCondition.java @@ -98,7 +98,8 @@ public Boolean execute(List spoList, Map context, TreeL private boolean absent(Map context) throws Exception { if (qlExpress.toLowerCase().contains("get_value") - || qlExpress.toLowerCase().contains("get_spo")) { + || qlExpress.toLowerCase().contains("get_spo") + || qlExpress.toLowerCase().contains("rule_value")) { return false; } Map> vars = varsCache.get(qlExpress); diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/visitor/RuleExecutor.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/visitor/RuleExecutor.java index f30d2f38f..6a0633b58 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/visitor/RuleExecutor.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/visitor/RuleExecutor.java @@ -24,6 +24,15 @@ import java.util.Map; public class RuleExecutor implements RuleNodeVisitor { + private boolean strictMode = true; + + public RuleExecutor() { + this.strictMode = false; + } + + public RuleExecutor(boolean strictMode) { + this.strictMode = strictMode; + } @Override public Boolean visit( @@ -31,7 +40,11 @@ public Boolean visit( Boolean ret = null; for (Node child : node.getChildren()) { Boolean c = child.accept(spoList, context, this, logger.addChild(child.toString())); - c = c == null ? true : c; + if (strictMode) { + c = c == null ? false : c; + } else { + c = c == null ? true : c; + } if (ret == null) { ret = c; } else { @@ -49,7 +62,11 @@ public Boolean visit( Boolean ret = null; for (Node child : node.getChildren()) { Boolean c = child.accept(spoList, context, this, logger.addChild(child.toString())); - c = c == null ? true : c; + if (strictMode) { + c = c == null ? false : c; + } else { + c = c == null ? true : c; + } if (ret == null) { ret = c; } else { @@ -67,7 +84,11 @@ public Boolean visit( Boolean ret = null; Node child = node.getChild(); Boolean r = child.accept(spoList, context, this, logger); - r = r == null ? true : r; + if (strictMode) { + r = r == null ? false : r; + } else { + r = r == null ? true : r; + } ret = !r; logger.log(ret); logger.setCurrentNodeRst(ret); @@ -80,6 +101,10 @@ public Boolean visit( Boolean ret = node.execute(spoList, context, logger); logger.log(ret); logger.setCurrentNodeRst(ret); - return ret == null ? true : ret; + if (strictMode) { + return ret == null ? false : ret; + } else { + return ret == null ? true : ret; + } } } diff --git a/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/GraphUtil.java b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/GraphUtil.java index b82eeaa42..b89f6be50 100644 --- a/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/GraphUtil.java +++ b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/GraphUtil.java @@ -17,6 +17,7 @@ import com.antgroup.openspg.reasoner.common.constants.Constants; import com.antgroup.openspg.reasoner.common.graph.edge.Direction; import com.antgroup.openspg.reasoner.common.graph.edge.IEdge; +import com.antgroup.openspg.reasoner.common.graph.edge.SPO; import com.antgroup.openspg.reasoner.common.graph.edge.impl.Edge; import com.antgroup.openspg.reasoner.common.graph.property.IProperty; import com.antgroup.openspg.reasoner.common.graph.property.IVersionProperty; @@ -44,8 +45,14 @@ public static Edge makeEdge( valueMap.put(0L, entry.getValue()); props.put(entry.getKey(), valueMap); } + SPO spo = new SPO(from.getId().getType(), edgeType, to.getId().getType()); return new Edge<>( - from.getId(), to.getId(), new VertexVersionProperty(props), 0L, Direction.OUT, edgeType); + from.getId(), + to.getId(), + new VertexVersionProperty(props), + 0L, + Direction.OUT, + spo.toString()); } public static Vertex makeVertex(String id, String type, Object... kvs) { diff --git a/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/MedTests.java b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/MedTests.java index 9a5d9023b..a837b2883 100644 --- a/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/MedTests.java +++ b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/MedTests.java @@ -13,36 +13,35 @@ package com.antgroup.openspg.reasoner.thinker; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.TypeReference; +import com.antgroup.openspg.reasoner.common.constants.Constants; import com.antgroup.openspg.reasoner.common.graph.property.IProperty; -import com.antgroup.openspg.reasoner.common.graph.property.impl.VertexVersionProperty; import com.antgroup.openspg.reasoner.common.graph.vertex.IVertexId; import com.antgroup.openspg.reasoner.common.graph.vertex.impl.Vertex; import com.antgroup.openspg.reasoner.graphstate.GraphState; -import com.antgroup.openspg.reasoner.graphstate.impl.MemGraphState; import com.antgroup.openspg.reasoner.thinker.catalog.ResourceLogicCatalog; import com.antgroup.openspg.reasoner.thinker.engine.DefaultThinker; import com.antgroup.openspg.reasoner.thinker.logic.Result; import com.antgroup.openspg.reasoner.thinker.logic.graph.Entity; +import com.antgroup.openspg.reasoner.thinker.logic.graph.Node; import com.antgroup.openspg.reasoner.thinker.logic.graph.Predicate; import com.antgroup.openspg.reasoner.thinker.logic.graph.Value; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import org.junit.Assert; import org.junit.Test; public class MedTests { private GraphState buildGraphState() { - GraphState graphState = new MemGraphState(); - Vertex vertex1 = - new Vertex<>( - IVertexId.from("尿酸", "Med.Examination"), - new VertexVersionProperty( - "highExplain", "highExplain desc", "lowExplain", "lowExplain desc")); - graphState.addVertex(vertex1); - - return graphState; + GraphUtil.makeVertex( + "尿酸", + "Med.Examination", + "highExplain", + "highExplain desc", + "lowExplain", + "lowExplain desc"); + return GraphUtil.buildMemState(Arrays.asList(vertex1), new LinkedList<>()); } @Test @@ -87,4 +86,35 @@ public void testHigh() { context); Assert.assertTrue(triples.size() == 2); } + + @Test + public void testStrict() { + ResourceLogicCatalog logicCatalog = new ResourceLogicCatalog("/Medical.txt"); + logicCatalog.init(); + Thinker thinker = new DefaultThinker(buildGraphState(), logicCatalog); + Map context = new HashMap<>(); + context.put("value", "阳性"); + context.put(Constants.SPG_REASONER_THINKER_STRICT, true); + List triples = + thinker.find( + new Entity("尿酸", "Med.Examination"), + new Predicate("abnormalRule"), + new Value(), + context); + Assert.assertTrue(triples.size() == 0); + } + + @Test + public void testHepatitis() { + ResourceLogicCatalog logicCatalog = new ResourceLogicCatalog("/Medical.txt"); + logicCatalog.init(); + Thinker thinker = new DefaultThinker(buildGraphState(), logicCatalog); + String str = + "{\"spg.reasoner.thinker.strict\":true,\"乙肝表面抗原\":\"225.000\",\"乙肝表面抗原_lower\":\"0\",\"乙肝表面抗原_upper\":\"0.5\",\"乙肝表面抗体\":\"0.1\",\"乙肝表面抗体_lower\":\"0\",\"乙肝表面抗体_upper\":\"10\",\"乙肝e抗原\":\"69.000\",\"乙肝e抗原_lower\":\"0\",\"乙肝e抗原_upper\":\"0.5\",\"乙肝e抗体\":\"0.00\",\"乙肝e抗体_lower\":\"0\",\"乙肝e抗体_upper\":\"0.2\",\"乙肝核心抗体\":\"4.050\",\"乙肝核心抗体_lower\":\"0\",\"乙肝核心抗体_upper\":\"0.9\"}"; + Map context = + JSONObject.parseObject(str, new TypeReference>() {}); + List triples = + thinker.find(null, new Predicate("确诊"), new Node("Medical.DiseaseTerm"), context); + Assert.assertTrue(triples.size() == 1); + } } diff --git a/reasoner/thinker/src/test/resources/Hypertension.txt b/reasoner/thinker/src/test/resources/Hypertension.txt index 0dd1fb5a1..9da6e8297 100644 --- a/reasoner/thinker/src/test/resources/Hypertension.txt +++ b/reasoner/thinker/src/test/resources/Hypertension.txt @@ -1,63 +1,75 @@ Define (血压水平分级/`1级高血压`) { - R1: 收缩压>=140 or 舒张压>=90 + 收缩压>=140 or 舒张压>=90 } Define (血压水平分级/`2级高血压`) { - R1: 收缩压>=160 or 舒张压>=100 + 收缩压>=160 or 舒张压>=100 } Define (血压水平分级/`血压正常高值`) { - R1: 收缩压>=120 or 舒张压>=80 + 收缩压>=120 or 舒张压>=80 } Define (疾病/`高血压`) { - R1: 血压水平分级/`1级高血压` or 血压水平分级/`2级高血压` + 血压水平分级/`1级高血压` or 血压水平分级/`2级高血压` } Define (疾病/`肥胖`) { - R1: BMI>=30 + BMI>=30 } Define (疾病/`慢性肾病3期`) { - R1: GFR>=30 + GFR>=30 } Define (高血压分层/`心血管危险因素-肥胖`) { - R1: 疾病/`肥胖` - R2: 疾病/`腹型肥胖` + 疾病/`肥胖` +} + +Define (高血压分层/`心血管危险因素-肥胖`) { + 疾病/`腹型肥胖` } Define (高血压分层/`靶器官损害-肾脏`) { - R1: 疾病/`慢性肾病3期` + 疾病/`慢性肾病3期` +} + +Define (高血压分层/`临床并发症-心脏疾病`) { + 疾病/`冠心病` } Define (高血压分层/`临床并发症-心脏疾病`) { - R1: 疾病/`冠心病` - R2: 疾病/`心力衰竭` - R3: 疾病/`心房颤动` + 疾病/`心力衰竭` +} + +Define (高血压分层/`临床并发症-心脏疾病`) { + 疾病/`心房颤动` } Define (高血压分层/`心血管危险因素`) { - R1: 高血压分级/`心血管危险因素-肥胖` + 高血压分级/`心血管危险因素-肥胖` } Define (高血压分层/`靶器官损害`) { - R1: 高血压分级/`靶器官损害-肾脏` + 高血压分级/`靶器官损害-肾脏` } Define (高血压分层/`临床并发症`) { - R1: 高血压分级/`临床并发症-心脏疾病` + 高血压分级/`临床并发症-心脏疾病` +} + +Define (药品/`多药方案`) { + 收缩压>=160 and 舒张压>=100 } Define (药品/`多药方案`) { - R1: 收缩压>=160 and 舒张压>=100 - R2: 收缩压-目标收缩压上界>=20 + 收缩压-目标收缩压上界>=20 } -Define ()-[:基本用药方案]->(:药品/`ACEI+噻嗪类利尿剂`) { - R1: 疾病/`高血压` and 药品/`多药方案` +Define ()-[:基本用药方案]->(:药品/`ACEI` + 药品/`噻嗪类利尿剂`) { + 疾病/`高血压` and 药品/`多药方案` } -Define ()-[:基本用药方案]->(:药品/`ARB+噻嗪类利尿剂`) { - R1: 疾病/`高血压` and 药品/`多药方案` +Define ()-[:基本用药方案]->(:药品/`ARB` + 药品/`噻嗪类利尿剂`) { + 疾病/`高血压` and 药品/`多药方案` } \ No newline at end of file diff --git a/reasoner/thinker/src/test/resources/Medical.txt b/reasoner/thinker/src/test/resources/Medical.txt index 28bc9606e..98db803ed 100644 --- a/reasoner/thinker/src/test/resources/Medical.txt +++ b/reasoner/thinker/src/test/resources/Medical.txt @@ -24,4 +24,20 @@ Define (a:Med.Examination)-[:abnormalRule]->(c: string) { Define (a:Med.Examination)-[:abnormalRule]->(c: string) { R1: (a)-[:abnormalValue]->(b: Med.ExaminationResult/`偏低`) AND (a)-[: lowExplain]->(c) +} + +Define()-[: 确诊]->(: Medical.DiseaseTerm/`乙型肝炎大三阳`) { + r: (: Medical.ExaminationTerm/`乙肝表面抗原`)-[: abnormalValue]->(: Medical.AbnormalExaminationIndicator/`阳性`) and (: Medical.ExaminationTerm/`乙肝e抗原`)-[: abnormalValue]->(: Medical.AbnormalExaminationIndicator/`阳性`) and (: Medical.ExaminationTerm/`乙肝核心抗体`)-[: abnormalValue]->(: Medical.AbnormalExaminationIndicator/`阳性`) +} + +Define (a:Medical.ExaminationTerm/`乙肝表面抗原`)-[:abnormalValue]->(c: Medical.AbnormalExaminationIndicator/`阳性`) { + R1: (乙肝表面抗原>rule_value(乙肝表面抗原_upper != null, 乙肝表面抗原_upper, 1)) +} + +Define (a:Medical.ExaminationTerm/`乙肝e抗原`)-[:abnormalValue]->(c: Medical.AbnormalExaminationIndicator/`阳性`) { + R1: (乙肝e抗原>rule_value(乙肝e抗原_upper != null, 乙肝e抗原_upper, 2.1)) +} + +Define (a:Medical.ExaminationTerm/`乙肝核心抗体`)-[:abnormalValue]->(c: Medical.AbnormalExaminationIndicator/`阳性`) { + R1: (乙肝核心抗体>rule_value(乙肝核心抗体_upper != null, 乙肝核心抗体_upper, 2.1)) } \ No newline at end of file From 329814bbd98a4dea3d925ed2041b49489701b15f Mon Sep 17 00:00:00 2001 From: matthewhyx Date: Fri, 12 Jul 2024 17:31:57 +0800 Subject: [PATCH 18/29] feat(schema): support maintain reasoning rule --- python/knext/knext/schema/marklang/concept_rule_ml.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/knext/knext/schema/marklang/concept_rule_ml.py b/python/knext/knext/schema/marklang/concept_rule_ml.py index ba57afcf8..bca26bf64 100644 --- a/python/knext/knext/schema/marklang/concept_rule_ml.py +++ b/python/knext/knext/schema/marklang/concept_rule_ml.py @@ -306,7 +306,7 @@ def submit_rule(self): subject_concept_type_name="Thing" if len( self.src_concept) == 0 else f"{self.namespace}.{self.src_concept[0]}", subject_concept_name="1" if len(self.src_concept) == 0 else self.src_concept[1], - predicate_name="_conclude" if self.predicate is None else self.predicate, + predicate_name="conclude" if self.predicate is None else self.predicate, object_concept_type_name=f"{self.namespace}.{self.dst_concept[0]}", object_concept_name=self.dst_concept[1], semantic_type="REASONING_CONCEPT", @@ -322,7 +322,7 @@ def submit_rule(self): subject_concept_type_name="Thing" if len( self.src_concept) == 0 else f"{self.namespace}.{self.src_concept[0]}", subject_concept_name="1" if len(self.src_concept) == 0 else self.src_concept[1], - predicate_name="_conclude" if self.predicate is None else self.predicate, + predicate_name="conclude" if self.predicate is None else self.predicate, object_concept_type_name=f"{self.namespace}.{self.dst_concept[0]}", object_concept_name=self.dst_concept[1], semantic_type="REASONING_CONCEPT" From cde69953c538b25dad9dc991b2e729b7054fc584 Mon Sep 17 00:00:00 2001 From: wenchengyao Date: Thu, 16 May 2024 17:57:47 +0800 Subject: [PATCH 19/29] fix(reasoner): fix findAllTypesAllowIsolateVertex (#249) --- .../openspg/reasoner/parser/OpenSPGDslParser.scala | 2 +- .../openspg/reasoner/parser/OpenSPGDslParserTest.scala | 10 ++++++++++ .../antgroup/openspg/reasoner/util/LoaderUtil.scala | 8 ++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/reasoner/kgdsl-parser/src/main/scala/com/antgroup/openspg/reasoner/parser/OpenSPGDslParser.scala b/reasoner/kgdsl-parser/src/main/scala/com/antgroup/openspg/reasoner/parser/OpenSPGDslParser.scala index e5afb5c1f..3012a23d8 100644 --- a/reasoner/kgdsl-parser/src/main/scala/com/antgroup/openspg/reasoner/parser/OpenSPGDslParser.scala +++ b/reasoner/kgdsl-parser/src/main/scala/com/antgroup/openspg/reasoner/parser/OpenSPGDslParser.scala @@ -85,7 +85,7 @@ class OpenSPGDslParser extends ParserInterface { */ override def parse(text: String): Block = { val blocks = parseMultipleStatement(text) - if (blocks.size > 1) { + if (blocks.size != 1) { throw KGDSLOneTaskException("dsl = " + text + "blocks num is " + blocks.size) } blocks.head diff --git a/reasoner/kgdsl-parser/src/test/scala/com/antgroup/openspg/reasoner/parser/OpenSPGDslParserTest.scala b/reasoner/kgdsl-parser/src/test/scala/com/antgroup/openspg/reasoner/parser/OpenSPGDslParserTest.scala index 62cb509ff..af6a127a8 100644 --- a/reasoner/kgdsl-parser/src/test/scala/com/antgroup/openspg/reasoner/parser/OpenSPGDslParserTest.scala +++ b/reasoner/kgdsl-parser/src/test/scala/com/antgroup/openspg/reasoner/parser/OpenSPGDslParserTest.scala @@ -184,6 +184,16 @@ class OpenSPGDslParserTest extends AnyFunSpec { } } + it("test exception2") { + val dsl = """test""".stripMargin + try { + parser.parse(dsl) + } catch { + case ex: KGDSLOneTaskException => + ex.getMessage.contains("num is 0") should equal(true) + } + } + it("opChainTest") { val chain = OpChainExpr(Filter(BinaryOpExpr(BNotEqual, Ref("a"), Ref("b"))), null) diff --git a/reasoner/runner/runner-common/src/main/scala/com/antgroup/openspg/reasoner/util/LoaderUtil.scala b/reasoner/runner/runner-common/src/main/scala/com/antgroup/openspg/reasoner/util/LoaderUtil.scala index f2e4eeaf0..525ae2189 100644 --- a/reasoner/runner/runner-common/src/main/scala/com/antgroup/openspg/reasoner/util/LoaderUtil.scala +++ b/reasoner/runner/runner-common/src/main/scala/com/antgroup/openspg/reasoner/util/LoaderUtil.scala @@ -67,6 +67,14 @@ object LoaderUtil { private def findAllTypesAllowIsolateVertex(logicalPlan: LogicalOperator): Set[String] = { logicalPlan.transform[Set[String]] { + case (PatternScan(_, pattern), typeSets) => + // 如果PatternScan只匹配单个起点,也需要加载孤点 + val connectionSet = pattern.topology.get(pattern.root.alias) + if (connectionSet.isEmpty || connectionSet.get.isEmpty) { + typeSets.flatten.toSet ++ pattern.root.typeNames + } else { + typeSets.flatten.toSet + } case (LinkedExpand(_, linkedEdgePattern), typeSets) => typeSets.flatten.toSet ++ linkedEdgePattern.src.typeNames case (_, typeSets) => typeSets.flatten.toSet From a25ce5e9c7d6f4c5bc4de1370c92b13df6dd0105 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=9F=B9=E9=BE=99?= Date: Tue, 21 May 2024 15:08:02 +0800 Subject: [PATCH 20/29] feat(reasoner): support trace execution info (#237) Co-authored-by: wenchengyao --- .../semantic/rules/MetaConceptExplain.scala | 20 +++--- .../runner/local/LocalReasonerRunner.java | 4 +- .../local/model/LocalReasonerResult.java | 19 +++++- .../reasoner/runner/local/rdg/LocalRDG.java | 66 ++++++++++++++++--- .../reasoner/runner/local/rdg/LocalRow.java | 3 +- .../runner/local/LocalReasonerResultTest.java | 2 +- .../recorder/action/DebugInfoWithStartId.java | 21 ++++-- .../warehouse/utils/WareHouseUtils.java | 10 +++ 8 files changed, 112 insertions(+), 33 deletions(-) diff --git a/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/validate/semantic/rules/MetaConceptExplain.scala b/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/validate/semantic/rules/MetaConceptExplain.scala index bb0582342..ae95e68b6 100644 --- a/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/validate/semantic/rules/MetaConceptExplain.scala +++ b/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/validate/semantic/rules/MetaConceptExplain.scala @@ -14,9 +14,10 @@ package com.antgroup.openspg.reasoner.lube.logical.validate.semantic.rules import com.antgroup.openspg.reasoner.lube.block.{Block, MatchBlock} -import com.antgroup.openspg.reasoner.lube.catalog.{SemanticPropertyGraph, SemanticRule} +import com.antgroup.openspg.reasoner.lube.catalog.SemanticPropertyGraph import com.antgroup.openspg.reasoner.lube.common.pattern.{ Connection, + EntityElement, GraphPattern, PatternElement } @@ -33,9 +34,9 @@ object MetaConceptExplain extends Explain { } else { val newPatterns = patterns.map { p => val pattern = p._2.graphPattern - val metaConceptEdges = - pattern.edges.values.filter(edge => - edge.exists(_.relTypes.contains("belongTo")) && !edge.exists(_.target.contains('/'))) + val metaConceptEdges = pattern.edges.values.filter(edge => + edge.exists(_.relTypes.contains("belongTo")) && !edge.exists(e => + pattern.nodes(e.target).isInstanceOf[EntityElement])) if (metaConceptEdges.isEmpty) { p } else { @@ -44,11 +45,8 @@ object MetaConceptExplain extends Explain { metaConceptEdges.foreach(e => parseMetaConcept(kg, e, pattern, metaConceptMap)) val newNodes = pattern.nodes.map(n => if (metaConceptMap.contains(n._1)) { - n.copy( - n._1, - PatternElement(n._1, metaConceptMap(n._1), n._2.rule))} - else n - ) + n.copy(n._1, PatternElement(n._1, metaConceptMap(n._1), n._2.rule)) + } else n) (p._1, p._2.copy(graphPattern = pattern.copy(nodes = newNodes))) } } @@ -72,8 +70,8 @@ object MetaConceptExplain extends Explain { if (!metaConceptMap.contains(targetAlias)) { metaConceptMap(targetAlias) = Set.empty } - val metaConcepts = metaConceptMap(targetAlias).++(matchedRules.map(r => - r.split("_belongTo_").last)) + val metaConcepts = + metaConceptMap(targetAlias).++(matchedRules.map(r => r.split("_belongTo_").last)) metaConceptMap.put(targetAlias, metaConcepts) } } diff --git a/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/LocalReasonerRunner.java b/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/LocalReasonerRunner.java index 9dfb93e84..a0f3157a7 100644 --- a/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/LocalReasonerRunner.java +++ b/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/LocalReasonerRunner.java @@ -174,9 +174,7 @@ private LocalReasonerResult doRun(LocalReasonerTask task) { mockLocalGraphLoader.load(); } - if (task.getParams().containsKey(ConfigKey.KG_REASONER_DEBUG_TRACE_ENABLE)) { - task.setExecutionRecorder(new DefaultRecorder()); - } + task.setExecutionRecorder(new DefaultRecorder()); if (physicalOpRoot instanceof Select) { LocalRow row = (LocalRow) ((Select) physicalOpRoot).row(); diff --git a/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/model/LocalReasonerResult.java b/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/model/LocalReasonerResult.java index 00e61f521..195493050 100644 --- a/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/model/LocalReasonerResult.java +++ b/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/model/LocalReasonerResult.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; import org.apache.commons.collections4.CollectionUtils; public class LocalReasonerResult { @@ -30,6 +31,8 @@ public class LocalReasonerResult { private final String debugTraceInfo; + private List> debugWithStartNodeInfos; + // select result private final List columns; private final List rows; @@ -68,7 +71,8 @@ public LocalReasonerResult( List> vertexList, List> edgeList, boolean graphResult, - String debugTraceInfo) { + String debugTraceInfo, + List> debugWithStartNodeInfos) { this.columns = null; this.rows = null; this.graphResult = graphResult; @@ -76,6 +80,7 @@ public LocalReasonerResult( this.edgeList = edgeList; this.errMsg = ""; this.debugTraceInfo = debugTraceInfo; + this.debugWithStartNodeInfos = debugWithStartNodeInfos; } /** output graph and row */ @@ -85,7 +90,8 @@ public LocalReasonerResult( List> vertexList, List> edgeList, boolean graphResult, - String debugTraceInfo) { + String debugTraceInfo, + List> debugWithStartNodeInfos) { this.columns = columns; this.rows = rows; this.graphResult = graphResult; @@ -93,6 +99,15 @@ public LocalReasonerResult( this.edgeList = edgeList; this.errMsg = ""; this.debugTraceInfo = debugTraceInfo; + this.debugWithStartNodeInfos = debugWithStartNodeInfos; + } + + public void setDebugWithStartNodeInfos(List> debugWithStartNodeInfos) { + this.debugWithStartNodeInfos = debugWithStartNodeInfos; + } + + public List> getDebugWithStartNodeInfos() { + return debugWithStartNodeInfos; } /** diff --git a/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/rdg/LocalRDG.java b/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/rdg/LocalRDG.java index e84297541..0ac76b1d5 100644 --- a/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/rdg/LocalRDG.java +++ b/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/rdg/LocalRDG.java @@ -14,6 +14,7 @@ package com.antgroup.openspg.reasoner.runner.local.rdg; import com.alibaba.fastjson.JSON; +import com.antgroup.openspg.common.util.StringUtils; import com.antgroup.openspg.reasoner.common.Utils; import com.antgroup.openspg.reasoner.common.constants.Constants; import com.antgroup.openspg.reasoner.common.exception.NotImplementedException; @@ -124,6 +125,8 @@ public class LocalRDG extends RDG { private final String startVertexAlias; + private String curRdgStartVertexAlias; + /** kg graph and it's schema */ private java.util.List> kgGraphList = new ArrayList<>(); @@ -165,7 +168,11 @@ private java.util.Set getStartId(java.util.List> k java.util.Set startIdSet = new HashSet<>(); for (KgGraph kgGraph : kgGraphList) { java.util.List> startVertexList = - kgGraph.getVertex(this.startVertexAlias); + kgGraph.getVertex(this.curRdgStartVertexAlias); + for (IVertex startId : startVertexList) { + startIdSet.add(startId.getId()); + } + startVertexList = kgGraph.getVertex(this.startVertexAlias); for (IVertex startId : startVertexList) { startIdSet.add(startId.getId()); } @@ -230,6 +237,7 @@ public LocalRDG( @Override public LocalRDG patternScan(Pattern pattern) { long startTime = System.currentTimeMillis(); + this.curRdgStartVertexAlias = WareHouseUtils.getPatternScanRootAlias(pattern); java.util.List rootVertexRuleList = WareHouseUtils.getVertexRuleList(pattern); java.util.Map> dstVertexRuleMap = WareHouseUtils.getDstVertexRuleList(pattern); @@ -1067,13 +1075,18 @@ private void addProperty(String alias, Field property, Boolean isLast) { + property.toString()); } - private java.util.Map getProcessInfo(DebugInfoWithStartId startId, String bizId) { + private java.util.Map getProcessInfo( + DebugInfoWithStartId startId, String bizId, String targetLabel, String targetId) { java.util.Map processInfo = startId.toJsonObj(); + if (StringUtils.isNotBlank(targetLabel)) { + processInfo.put("targetLabel", targetLabel.split("/")[0]); + processInfo.put("targetId", targetId); + } java.util.Map startIdInfo = new HashMap<>(); startIdInfo.put("bizId", bizId); startIdInfo.put("label", startId.getVertexId().getType()); startIdInfo.put("id", startId.getVertexId()); - processInfo.put("start_node", startIdInfo); + processInfo.put("startNode", startIdInfo); return processInfo; } @@ -1091,16 +1104,24 @@ private void addRelation(AddPredicate addPredicate) { edge.getValue() .put( - "__process__", - getProcessInfo(startId, (String) edge.getValue().get(Constants.EDGE_FROM_ID_KEY))); + "process", + getProcessInfo( + startId, + (String) edge.getValue().get(Constants.EDGE_FROM_ID_KEY), + (String) edge.getValue().get(Constants.EDGE_TO_ID_TYPE_KEY), + (String) edge.getValue().get(Constants.EDGE_TO_ID_KEY))); } if (debugInfoMap.containsKey(edge.getTargetId())) { DebugInfoWithStartId startId = debugInfoMap.get(edge.getTargetId()); edge.getValue() .put( - "__process__", - getProcessInfo(startId, (String) edge.getValue().get(Constants.EDGE_TO_ID_KEY))); + "process", + getProcessInfo( + startId, + (String) edge.getValue().get(Constants.EDGE_TO_ID_KEY), + (String) edge.getValue().get(Constants.EDGE_FROM_ID_TYPE_KEY), + (String) edge.getValue().get(Constants.EDGE_FROM_ID_KEY))); } // add to result list this.resultEdgeSet.add(edge); @@ -1351,23 +1372,50 @@ public PartialGraphPattern getKgGraphSchema() { return this.kgGraphSchema; } + private java.util.List> generateStartNodeDebugInfo() { + java.util.Map debugInfoMap = + this.executionRecorder.getCurStageDebugInfo(); + java.util.List> debugStartNodeINfos = new ArrayList<>(); + for (IVertexId id : debugInfoMap.keySet()) { + DebugInfoWithStartId debugInfoWithStartId = debugInfoMap.get(id); + IVertex vertex = + graphState.getVertex(debugInfoWithStartId.getVertexId(), null); + if (vertex == null) { + continue; + } + debugStartNodeINfos.add( + getProcessInfo( + debugInfoWithStartId, + vertex.getValue().get(Constants.NODE_ID_KEY).toString(), + "", + "")); + } + return debugStartNodeINfos; + } /** get ddl result */ public LocalReasonerResult getResult() { + java.util.List> debugStartNodeINfos = + generateStartNodeDebugInfo(); return new LocalReasonerResult( Lists.newArrayList(resultVertexSet), Lists.newArrayList(resultEdgeSet), true, - this.executionRecorder.toReadableString()); + this.executionRecorder.toReadableString(), + debugStartNodeINfos); } /** add graph to traversal graph */ private LocalReasonerResult getRDGGraph() { if (!isCarryTraversalGraph) { + java.util.List> debugStartNodeINfos = + generateStartNodeDebugInfo(); + return new LocalReasonerResult( Lists.newArrayList(), Lists.newArrayList(), false, - this.executionRecorder.toReadableString()); + this.executionRecorder.toReadableString(), + debugStartNodeINfos); } LocalReasonerResult localReasonerResult = getResult(); this.kgGraphList.forEach( diff --git a/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/rdg/LocalRow.java b/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/rdg/LocalRow.java index 99445ec32..2679d66c4 100644 --- a/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/rdg/LocalRow.java +++ b/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/rdg/LocalRow.java @@ -73,6 +73,7 @@ public LocalReasonerResult getResult() { graphRst.getVertexList(), graphRst.getEdgeList(), graphRst.isGraphResult(), - graphRst.getDebugTraceInfo()); + graphRst.getDebugTraceInfo(), + graphRst.getDebugWithStartNodeInfos()); } } diff --git a/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/LocalReasonerResultTest.java b/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/LocalReasonerResultTest.java index e2f3d1bfe..6c8514e93 100644 --- a/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/LocalReasonerResultTest.java +++ b/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/LocalReasonerResultTest.java @@ -45,7 +45,7 @@ public void localReasonerResultTest() { Direction.OUT)); boolean ddlResult = true; LocalReasonerResult localReasonerResult = - new LocalReasonerResult(vertexList, edgeList, ddlResult, ""); + new LocalReasonerResult(vertexList, edgeList, ddlResult, "", Lists.newArrayList()); Assert.assertTrue(localReasonerResult.isGraphResult()); System.out.println(localReasonerResult); diff --git a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/DebugInfoWithStartId.java b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/DebugInfoWithStartId.java index bedfeea7b..e72fe6502 100644 --- a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/DebugInfoWithStartId.java +++ b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/DebugInfoWithStartId.java @@ -36,16 +36,25 @@ public IVertexId getVertexId() { public Map toJsonObj() { Map result = new HashMap<>(); - Map hitRuleInfo = new HashMap<>(); + List> hitRuleInfos = new ArrayList<>(); + for (Rule r : hitRules) { - hitRuleInfo.put(r.getName(), StringUtils.join(WareHouseUtils.getRuleList(r), ",")); + Map hitRuleInfoDetail = new HashMap<>(); + hitRuleInfoDetail.put("ruleName", r.getName()); + hitRuleInfoDetail.put("ruleValue", StringUtils.join(WareHouseUtils.getRuleList(r), ",")); + hitRuleInfoDetail.put("hitValue", ""); + hitRuleInfos.add(hitRuleInfoDetail); } - result.put("hit_rule", hitRuleInfo); - Map failedRuleInfo = new HashMap<>(); + result.put("hitRule", hitRuleInfos); + List> failedRuleInfos = new ArrayList<>(); for (Rule r : failedRules) { - failedRuleInfo.put(r.getName(), StringUtils.join(WareHouseUtils.getRuleList(r), ",")); + Map failedRuleInfoDetail = new HashMap<>(); + failedRuleInfoDetail.put("ruleName", r.getName()); + failedRuleInfoDetail.put("ruleValue", StringUtils.join(WareHouseUtils.getRuleList(r), ",")); + failedRuleInfoDetail.put("hitValue", ""); + failedRuleInfos.add(failedRuleInfoDetail); } - result.put("failed_rule", failedRuleInfo); + result.put("failedRule", failedRuleInfos); return result; } 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 bc73cd239..569d1f5c5 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 @@ -71,6 +71,16 @@ public static List getPatternRuleList(Pattern pattern) { return patternRuleList; } + /** + * get start vertex alias name + * + * @param pattern + * @return + */ + public static String getPatternScanRootAlias(Pattern pattern) { + return pattern.root().alias(); + } + /** * get vertex rule string * From bfa462f9f7c4b58be9ac906536af1642187ee87e Mon Sep 17 00:00:00 2001 From: FishJoy Date: Wed, 12 Jun 2024 16:48:02 +0800 Subject: [PATCH 21/29] feat(reasoner): support spo inference (#280) Co-authored-by: kejian --- .../openspg/reasoner/thinker/Thinker.java | 3 +- .../thinker/engine/DefaultThinker.java | 3 +- .../reasoner/thinker/engine/Graph.java | 4 +- .../reasoner/thinker/engine/GraphStore.java | 34 +- .../reasoner/thinker/engine/InfGraph.java | 307 ++++++++++++++++-- .../thinker/engine/MemTripleStore.java | 33 +- .../reasoner/thinker/logic/Evidence.java | 27 ++ .../reasoner/thinker/logic/LogicNetwork.java | 39 ++- .../reasoner/thinker/logic/graph/Element.java | 18 + .../reasoner/thinker/logic/graph/Entity.java | 35 +- .../reasoner/thinker/logic/graph/Node.java | 25 ++ .../thinker/logic/graph/Predicate.java | 29 ++ .../reasoner/thinker/logic/graph/Triple.java | 33 ++ .../reasoner/thinker/logic/graph/Value.java | 72 +++- .../thinker/logic/rule/TreeLogger.java | 13 +- .../logic/rule/exact/QlExpressCondition.java | 87 ++++- .../logic/rule/visitor/RuleExecutor.java | 9 +- .../thinker/qlexpress/QlExpressRunner.java | 121 +++++++ .../op/RichOperatorEqualsLessMore.java | 144 ++++++++ .../reasoner/thinker/ThinkerRuleParser.scala | 19 +- .../reasoner/thinker/DefaultThinkerTests.java | 109 ++----- .../openspg/reasoner/thinker/GraphUtil.java | 81 +++++ .../reasoner/thinker/HypertensionTest.java | 73 +++++ .../reasoner/thinker/InsuranceTests.java | 124 +++++++ .../openspg/reasoner/thinker/MedTests.java | 90 +++++ .../thinker/catalog/ResourceLogicCatalog.java | 90 +++++ .../thinker/logic/rule/QlExpressTest.java | 83 +++++ .../thinker/logic/rule/RuleExecutorTests.java | 13 - .../src/test/resources/Hypertension.txt | 63 ++++ .../src/test/resources/InsuranceRules.txt | 16 + .../thinker/src/test/resources/Medical.txt | 27 ++ .../thinker/SimplifyThinkerParserTest.scala | 30 +- .../openspg/reasoner/udf/rule/RuleRunner.java | 22 +- .../udf/rule/op/OperatorEqualsLessMore.java | 14 +- .../reasoner/udf/rule/op/OperatorGetSPO.java | 36 ++ 35 files changed, 1716 insertions(+), 210 deletions(-) create mode 100644 reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/Evidence.java create mode 100644 reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/qlexpress/QlExpressRunner.java create mode 100644 reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/qlexpress/op/RichOperatorEqualsLessMore.java create mode 100644 reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/GraphUtil.java create mode 100644 reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/HypertensionTest.java create mode 100644 reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/InsuranceTests.java create mode 100644 reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/MedTests.java create mode 100644 reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/catalog/ResourceLogicCatalog.java create mode 100644 reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/logic/rule/QlExpressTest.java create mode 100644 reasoner/thinker/src/test/resources/Hypertension.txt create mode 100644 reasoner/thinker/src/test/resources/InsuranceRules.txt create mode 100644 reasoner/thinker/src/test/resources/Medical.txt create mode 100644 reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/rule/op/OperatorGetSPO.java diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/Thinker.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/Thinker.java index 061106c90..492787d0a 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/Thinker.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/Thinker.java @@ -15,6 +15,7 @@ import com.antgroup.openspg.reasoner.thinker.logic.Result; import com.antgroup.openspg.reasoner.thinker.logic.graph.Element; +import com.antgroup.openspg.reasoner.thinker.logic.graph.Node; import java.util.List; import java.util.Map; @@ -25,5 +26,5 @@ public interface Thinker { List find(Element s, Element p, Element o, Map context); - List find(Element target, Map context); + List find(Node target, Map context); } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/DefaultThinker.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/DefaultThinker.java index 767bdb740..1954164f3 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/DefaultThinker.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/DefaultThinker.java @@ -19,6 +19,7 @@ import com.antgroup.openspg.reasoner.thinker.catalog.LogicCatalog; import com.antgroup.openspg.reasoner.thinker.logic.Result; import com.antgroup.openspg.reasoner.thinker.logic.graph.Element; +import com.antgroup.openspg.reasoner.thinker.logic.graph.Node; import com.antgroup.openspg.reasoner.thinker.logic.graph.Triple; import java.util.*; @@ -50,7 +51,7 @@ public List find(Element s, Element p, Element o, Map co } @Override - public List find(Element s, Map context) { + public List find(Node s, Map context) { this.infGraph.clear(); return this.infGraph.find(s, context); } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/Graph.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/Graph.java index d83fea9ba..670adbd01 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/Graph.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/Graph.java @@ -14,7 +14,7 @@ package com.antgroup.openspg.reasoner.thinker.engine; import com.antgroup.openspg.reasoner.thinker.logic.Result; -import com.antgroup.openspg.reasoner.thinker.logic.graph.Element; +import com.antgroup.openspg.reasoner.thinker.logic.graph.Node; import com.antgroup.openspg.reasoner.thinker.logic.graph.Triple; import java.util.List; import java.util.Map; @@ -24,5 +24,5 @@ public interface Graph { List find(Triple pattern, Map context); - List find(Element s, Map context); + List find(Node s, Map context); } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/GraphStore.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/GraphStore.java index 5e453564f..a0b6a6945 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/GraphStore.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/GraphStore.java @@ -22,10 +22,7 @@ import com.antgroup.openspg.reasoner.graphstate.GraphState; import com.antgroup.openspg.reasoner.thinker.logic.Result; import com.antgroup.openspg.reasoner.thinker.logic.graph.*; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; +import java.util.*; public class GraphStore implements Graph { private GraphState graphState; @@ -45,14 +42,16 @@ public List find(Triple pattern, Map context) { data = getTriple((Entity) pattern.getSubject(), Direction.OUT); } else if (pattern.getObject() instanceof Entity) { data = getTriple((Entity) pattern.getObject(), Direction.IN); + } else if (pattern.getSubject() instanceof Triple) { + data = getTriple((Triple) pattern.getSubject(), (Predicate) pattern.getPredicate()); } else { - throw new RuntimeException("Cannot support " + pattern); + return new ArrayList<>(); } return matchInGraph(pattern, data); } @Override - public List find(Element s, Map context) { + public List find(Node s, Map context) { return Collections.emptyList(); } @@ -61,8 +60,11 @@ protected List getTriple(Entity s, Direction direction) { if (direction == Direction.OUT) { IVertex vertex = this.graphState.getVertex(IVertexId.from(s.getId(), s.getType()), null); + if (vertex == null) { + return triples; + } for (String key : vertex.getValue().getKeySet()) { - triples.add(new Triple(s, new Predicate(key), new Value(key, vertex.getValue().get(key)))); + triples.add(new Triple(s, new Predicate(key), new Value(vertex.getValue().get(key)))); } } List> edges = @@ -74,6 +76,24 @@ protected List getTriple(Entity s, Direction direction) { return triples; } + protected List getTriple(Triple triple, Predicate predicate) { + List triples = new LinkedList<>(); + Entity s = (Entity) triple.getSubject(); + Predicate p = (Predicate) triple.getPredicate(); + Entity o = (Entity) triple.getObject(); + List> edges = + this.graphState.getEdges( + IVertexId.from(s.getId(), s.getType()), null, null, null, Direction.OUT); + for (IEdge edge : edges) { + if (edge.getTargetId().equals(IVertexId.from(o.getId(), o.getType())) + && edge.getType().equals(p.getName())) { + triples.add( + new Triple(triple, predicate, new Value(edge.getValue().get(predicate.getName())))); + } + } + return triples; + } + private Triple edgeToTriple(IEdge edge) { if (edge.getDirection() == Direction.OUT) { return new Triple( diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/InfGraph.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/InfGraph.java index 307662071..e67a40ec6 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/InfGraph.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/InfGraph.java @@ -16,26 +16,32 @@ import com.antgroup.openspg.reasoner.thinker.TripleStore; import com.antgroup.openspg.reasoner.thinker.logic.LogicNetwork; import com.antgroup.openspg.reasoner.thinker.logic.Result; -import com.antgroup.openspg.reasoner.thinker.logic.graph.Element; -import com.antgroup.openspg.reasoner.thinker.logic.graph.Entity; -import com.antgroup.openspg.reasoner.thinker.logic.graph.Triple; +import com.antgroup.openspg.reasoner.thinker.logic.graph.*; import com.antgroup.openspg.reasoner.thinker.logic.rule.ClauseEntry; import com.antgroup.openspg.reasoner.thinker.logic.rule.Rule; import com.antgroup.openspg.reasoner.thinker.logic.rule.TreeLogger; import com.antgroup.openspg.reasoner.thinker.logic.rule.visitor.RuleExecutor; import java.util.*; +import java.util.concurrent.LinkedBlockingDeque; import java.util.stream.Collectors; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class InfGraph implements Graph { + private static final Logger logger = LoggerFactory.getLogger(InfGraph.class); + private LogicNetwork logicNetwork; private TripleStore tripleStore; private GraphStore graphStore; + private Set recorder; public InfGraph(LogicNetwork logicNetwork, GraphStore graphStore) { this.logicNetwork = logicNetwork; this.tripleStore = new MemTripleStore(); this.graphStore = graphStore; + this.recorder = new HashSet<>(); } @Override @@ -45,17 +51,32 @@ public void init(Map param) { @Override public List find(Triple pattern, Map context) { + logger.info("InfGraph find pattern={}, context={}", pattern, context); + List result = new LinkedList<>(); prepareContext(context); // Step1: find pattern in graph List dataInGraph = graphStore.find(pattern, context); + logger.info("GraphStore find pattern={}, result={}", pattern, dataInGraph); if (CollectionUtils.isNotEmpty(dataInGraph)) { - return dataInGraph; + for (Result tri : dataInGraph) { + addTriple((Triple) tri.getData()); + result.add(tri); + } } - return inference(pattern, context); + recorder.add((Triple) pattern.cleanAlias()); + List infResult = inference(pattern, context); + logger.info("InfGraph infer pattern={}, result={}", pattern, infResult); + result.addAll(infResult); + return result; } @Override - public List find(Element s, Map context) { + public List find(Node s, Map context) { + prepareContext(context); + return inference(s, context); + } + + public List find(Entity s, Map context) { prepareContext(context); return inference(s, context); } @@ -75,44 +96,273 @@ private List inference(Element pattern, Map context) { for (Rule rule : logicNetwork.getBackwardRules(pattern)) { List body = rule.getBody().stream().map(ClauseEntry::toElement).collect(Collectors.toList()); - List data = prepareElements(body); + List> data = prepareElements(body, rule.getHead().toElement(), pattern, context); if (CollectionUtils.isEmpty(data)) { - continue; - } - TreeLogger traceLogger = new TreeLogger(rule.getRoot().toString()); - Boolean ret = - rule.getRoot() - .accept( - data.stream().map(Result::getData).collect(Collectors.toList()), - context, - new RuleExecutor(), - traceLogger); - if (ret) { - rst.add(new Result(rule.getHead().toElement(), traceLogger)); + TreeLogger traceLogger = new TreeLogger(rule.getRoot().toString()); + Boolean ret = + rule.getRoot().accept(new LinkedList<>(), context, new RuleExecutor(), traceLogger); + traceLogger.setCurrentNodeMsg(rule.getDesc()); + if (ret) { + Element ele = rule.getHead().toElement(); + rst.add(new Result(ele, traceLogger)); + if (ele instanceof Triple) { + addTriple((Triple) ele); + } else { + addEntity((Entity) ele); + } + } + } else { + for (List d : data) { + TreeLogger traceLogger = new TreeLogger(rule.getRoot().toString()); + List dList = d.stream().map(Result::getData).collect(Collectors.toList()); + Boolean ret = rule.getRoot().accept(dList, context, new RuleExecutor(), traceLogger); + List msg = + d.stream() + .map(Result::getTraceLog) + .filter(l -> l != null) + .map(TreeLogger::getCurrentNodeMsg) + .filter(m -> StringUtils.isNotBlank(m)) + .collect(Collectors.toList()); + List msgs = new LinkedList<>(msg); + if (StringUtils.isNotBlank(rule.getDesc())) { + msgs.add(rule.getDesc()); + } + traceLogger.setCurrentNodeMsg(StringUtils.join(msgs, ";")); + if (ret) { + Element ele = rule.getHead().toElement(); + rst.add(new Result(bindResult(dList, ele), traceLogger)); + if (ele instanceof Triple) { + addTriple((Triple) ele); + } else { + addEntity((Entity) ele); + } + } + } } } return rst; } - private List prepareElements(List body) { - List elements = new ArrayList<>(); - for (Element pattern : body) { - Collection spo = prepareElement(pattern); - if (spo != null && !spo.isEmpty()) { - elements.addAll(spo); + private Element bindResult(List data, Element pattern) { + Map map = new HashMap<>(); + for (Element e : data) { + if (e instanceof Entity || e instanceof Node) { + map.put(e.alias(), e); + } else if (e instanceof Triple) { + map.put(((Triple) e).getSubject().alias(), ((Triple) e).getSubject()); + map.put(((Triple) e).getObject().alias(), ((Triple) e).getObject()); + } + } + if (pattern instanceof Entity) { + return pattern; + } else if (pattern instanceof Node) { + return map.get(pattern.alias()); + } + Triple t = (Triple) pattern; + return new Triple( + map.getOrDefault(t.getSubject().alias(), ((Triple) pattern).getSubject()), + t.getPredicate(), + map.getOrDefault(t.getObject().alias(), ((Triple) pattern).getObject())); + } + + private List> prepareElements( + List body, Element head, Element pattern, Map context) { + List> elements = new ArrayList<>(); + Set choose = new HashSet<>(); + Queue starts = new LinkedBlockingDeque<>(); + Element start = getStart(pattern, head); + starts.add(start); + while (choose.size() < body.size()) { + Element s = starts.poll(); + if (s == null) { + break; + } + if (s instanceof Node && elements.isEmpty()) { + break; + } + for (Element e : body) { + if (choose.contains(e)) { + continue; + } + if (e instanceof Entity) { + choose.add(e); + if (CollectionUtils.isEmpty(elements)) { + elements.add(new LinkedList<>(prepareElement(e, context))); + } else { + for (List evidence : elements) { + evidence.addAll(prepareElement(e, context)); + } + } + } else { + Triple t = (Triple) e; + if (t.getSubject().alias().equals(s.alias())) { + starts.add(t.getObject()); + } else if (t.getObject().alias().equals(s.alias())) { + starts.add(t.getSubject()); + } else if (t.getSubject() instanceof Predicate) { + if (choose.stream() + .filter(ele -> ele instanceof Triple) + .filter(ele -> ((Triple) ele).getPredicate().alias() == t.getSubject().alias()) + .count() + == 0) { + continue; + } + } else { + continue; + } + choose.add(e); + if (CollectionUtils.isEmpty(elements)) { + Triple triple = buildTriple(null, s, t); + List> singeRst = prepareElement(null, triple, context); + if (CollectionUtils.isNotEmpty(singeRst)) { + elements.addAll(singeRst); + } + } else { + List> tmpElements = new LinkedList<>(); + for (List evidence : elements) { + Triple triple = buildTriple(evidence, s, t); + if (triple != null) { + List> singeRst = prepareElement(evidence, triple, context); + if (CollectionUtils.isNotEmpty(singeRst)) { + tmpElements.addAll(singeRst); + } + } + } + elements.clear(); + elements = tmpElements; + } + } } } return elements; } - private Collection prepareElement(Element pattern) { - Collection result; + private Triple buildTriple(List evidence, Element s, Triple triple) { + Entity entity = null; + Triple trip = null; + if (CollectionUtils.isEmpty(evidence)) { + entity = (Entity) s; + } else { + for (Result r : evidence) { + Element e = r.getData(); + if (triple.getSubject() instanceof Predicate) { + if (e instanceof Triple + && ((Triple) e).getPredicate().alias() == triple.getSubject().alias()) { + trip = (Triple) e; + } + } else { + if (e instanceof Entity && e.alias() == s.alias()) { + entity = (Entity) r.getData(); + } else if (e instanceof Triple && ((Triple) e).getSubject().alias() == s.alias()) { + entity = (Entity) ((Triple) e).getSubject(); + } else if (e instanceof Triple && ((Triple) e).getObject().alias() == s.alias()) { + entity = (Entity) ((Triple) e).getObject(); + } + } + } + } + + if (entity == null && trip == null) { + return null; + } + if (triple.getSubject().alias() == s.alias()) { + return new Triple(entity, triple.getPredicate(), triple.getObject()); + } else if (triple.getObject().alias() == s.alias()) { + return new Triple(triple.getSubject(), triple.getPredicate(), entity); + } else if (triple.getSubject() instanceof Predicate && trip != null) { + return new Triple(trip, triple.getPredicate(), triple.getObject()); + } else { + return null; + } + } + + private Element getStart(Element element, Element pattern) { + if (element instanceof Entity || element instanceof Node) { + return element.bind(pattern); + } else if (((Triple) element).getSubject() instanceof Entity) { + Element subject = ((Triple) pattern).getSubject(); + return ((Triple) element).getSubject().bind(subject); + } else { + Element object = ((Triple) pattern).getObject(); + return ((Triple) element).getObject().bind(object); + } + } + + private List> prepareElement( + List evidences, Triple pattern, Map context) { + List> rst = new LinkedList<>(); + if (CollectionUtils.isEmpty(evidences)) { + Collection curRst = prepareElement(pattern, context); + for (Result r : curRst) { + rst.add(new LinkedList<>(Arrays.asList(r))); + } + return rst; + } + Collection curRst = prepareElement(pattern, context); + for (Result r : curRst) { + List merged = new LinkedList<>(evidences); + merged.add(r); + if (reserve(merged)) { + rst.add(merged); + } + } + return rst; + } + + private Boolean reserve(List evidence) { + Map aliasToId = new HashMap<>(); + for (Result r : evidence) { + Element e = r.getData(); + if (e instanceof Entity) { + if (!aliasToId.containsKey(e.alias())) { + aliasToId.put(e.alias(), ((Entity) e).getId()); + } + if (!StringUtils.equals(((Entity) e).getId(), aliasToId.get(e.alias()))) { + return false; + } + } else if (!(((Triple) e).getSubject() instanceof Triple)) { + if (((Triple) e).getSubject() instanceof Entity) { + Entity s = (Entity) ((Triple) e).getSubject(); + if (!aliasToId.containsKey(s.alias())) { + aliasToId.put(s.alias(), s.getId()); + } + if (!StringUtils.equals(s.getId(), aliasToId.get(s.alias()))) { + return false; + } + } + if (((Triple) e).getObject() instanceof Entity) { + Entity o = (Entity) ((Triple) e).getObject(); + + if (!aliasToId.containsKey(o.alias())) { + aliasToId.put(o.alias(), o.getId()); + } + + if (!StringUtils.equals(o.getId(), aliasToId.get(o.alias()))) { + return false; + } + } + } + } + return true; + } + + private Collection prepareElement(Element pattern, Map context) { + Collection result = new LinkedList<>(); Collection spo = this.tripleStore.find(pattern); if (spo == null || spo.isEmpty()) { - result = find(pattern, null); + if (pattern instanceof Node) { + result = find((Node) pattern, context); + } else if (pattern instanceof Entity) { + result = find((Entity) pattern, context); + } else if (!recorder.contains(pattern.cleanAlias())) { + result = find((Triple) pattern, context); + } } else { result = spo.stream().map(e -> new Result(e, null)).collect(Collectors.toList()); } + for (Result r : result) { + r.setData(r.getData().bind(pattern)); + } return result; } @@ -126,5 +376,6 @@ public void addTriple(Triple triple) { public void clear() { this.tripleStore.clear(); + this.recorder.clear(); } } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/MemTripleStore.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/MemTripleStore.java index 3fb69356b..8fb2596df 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/MemTripleStore.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/MemTripleStore.java @@ -58,13 +58,22 @@ private Collection findEntity(Entity pattern) { } private Collection findTriple(Triple tripleMatch) { + Triple t = (Triple) tripleMatch.cleanAlias(); List elements = new LinkedList<>(); - if (tripleMatch.getSubject() instanceof Entity) { - elements.addAll(sToTriple.getOrDefault(tripleMatch.getSubject(), new LinkedList<>())); - } else if (tripleMatch.getObject() instanceof Entity) { - elements.addAll(oToTriple.getOrDefault(tripleMatch.getObject(), new LinkedList<>())); + if (t.getSubject() instanceof Entity || t.getSubject() instanceof Triple) { + for (Triple tri : sToTriple.getOrDefault(t.getSubject(), new LinkedList<>())) { + if (t.matches(tri)) { + elements.add(tri); + } + } + } else if (t.getObject() instanceof Entity) { + for (Triple tri : oToTriple.getOrDefault(t.getObject(), new LinkedList<>())) { + if (t.matches(tri)) { + elements.add(tri); + } + } } else { - throw new RuntimeException("Cannot support " + tripleMatch); + throw new RuntimeException("Cannot support " + t); } return elements; } @@ -76,18 +85,18 @@ private String getKey(Entity entity) { @Override public void addEntity(Entity entity) { String key = getKey(entity); - entities.put(key, entity); + entities.put(key, (Entity) entity.cleanAlias()); } @Override public void addTriple(Triple triple) { + Triple t = (Triple) triple.cleanAlias(); List sTriples = - sToTriple.computeIfAbsent(triple.getSubject(), (k) -> new LinkedList<>()); - sTriples.add(triple); - if (!(triple.getObject() instanceof Value)) { - List oTriples = - oToTriple.computeIfAbsent(triple.getSubject(), (k) -> new LinkedList<>()); - oTriples.add(triple); + sToTriple.computeIfAbsent(t.getSubject().cleanAlias(), (k) -> new LinkedList<>()); + sTriples.add(t); + if (!(t.getObject() instanceof Value)) { + List oTriples = oToTriple.computeIfAbsent(t.getObject(), (k) -> new LinkedList<>()); + oTriples.add(t); } } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/Evidence.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/Evidence.java new file mode 100644 index 000000000..3a681ce7a --- /dev/null +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/Evidence.java @@ -0,0 +1,27 @@ +/* + * 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.thinker.logic; + +import com.antgroup.openspg.reasoner.thinker.logic.graph.Element; +import java.util.List; +import lombok.Data; + +@Data +public class Evidence { + private List evidence; + + public Evidence(List evidence) { + this.evidence = evidence; + } +} diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/LogicNetwork.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/LogicNetwork.java index 1cfa3c328..6827d91fe 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/LogicNetwork.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/LogicNetwork.java @@ -19,39 +19,43 @@ import java.util.*; import java.util.stream.Collectors; import lombok.Data; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @Data public class LogicNetwork { - private Map ruleMap; - private Map> forwardRules; - private Map, Rule>> backwardRules; + private static final Logger logger = LoggerFactory.getLogger(LogicNetwork.class); + + private Map>> forwardRules; + private Map, List>> backwardRules; public LogicNetwork() { this.forwardRules = new HashMap<>(); this.backwardRules = new HashMap<>(); - this.ruleMap = new HashMap<>(); } public void addRule(Rule rule) { - if (!ruleMap.containsKey(rule.getName())) { - ruleMap.put(rule.getName(), rule); - } for (ClauseEntry body : rule.getBody()) { - Map rules = + Map> rules = forwardRules.computeIfAbsent(body.toElement(), (key) -> new HashMap<>()); - rules.put(rule.getHead().toElement(), rule); + List rList = + rules.computeIfAbsent(rule.getHead().toElement(), (k) -> new LinkedList<>()); + rList.add(rule); } - Map, Rule> rules = + Map, List> rules = backwardRules.computeIfAbsent(rule.getHead().toElement(), (key) -> new HashMap<>()); - rules.put( - rule.getBody().stream().map(ClauseEntry::toElement).collect(Collectors.toList()), rule); + List rList = + rules.computeIfAbsent( + rule.getBody().stream().map(ClauseEntry::toElement).collect(Collectors.toList()), + (k) -> new LinkedList<>()); + rList.add(rule); } public Collection getForwardRules(Element e) { Set rules = new HashSet<>(); - for (Map.Entry> entry : forwardRules.entrySet()) { + for (Map.Entry>> entry : forwardRules.entrySet()) { if (entry.getKey().matches(e)) { - rules.addAll(entry.getValue().values()); + entry.getValue().values().stream().forEach(list -> rules.addAll(list)); } } return rules; @@ -59,11 +63,12 @@ public Collection getForwardRules(Element e) { public Collection getBackwardRules(Element triple) { Set rules = new HashSet<>(); - for (Map.Entry, Rule>> entry : backwardRules.entrySet()) { - if (triple.matches(entry.getKey())) { - rules.addAll(entry.getValue().values()); + for (Map.Entry, List>> entry : backwardRules.entrySet()) { + if (entry.getKey().matches(triple)) { + entry.getValue().values().stream().forEach(list -> rules.addAll(list)); } } + logger.info("LogicNetwork getBackwardRules, pattern={}, rules={}", triple, rules.size()); return rules; } } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Element.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Element.java index febc71386..d5cd0024d 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Element.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Element.java @@ -13,6 +13,7 @@ package com.antgroup.openspg.reasoner.thinker.logic.graph; +import com.antgroup.openspg.reasoner.common.exception.UnsupportedOperationException; import java.io.Serializable; public abstract class Element implements Serializable { @@ -23,4 +24,21 @@ public abstract class Element implements Serializable { public boolean matches(Element other) { return equals(other); } + + public Element bind(Element pattern) { + return this; + } + + public String alias() { + throw new UnsupportedOperationException( + this.getClass().getSimpleName() + " cannot support", null); + } + + public Element cleanAlias() { + return this; + } + + public String shortString() { + return toString(); + } } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Entity.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Entity.java index 2eeac4ca5..d17461f1c 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Entity.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Entity.java @@ -102,13 +102,46 @@ public boolean equals(Object o) { && Objects.equals(alias, entity.alias); } + public boolean matches(Element other) { + if (other == null) { + return false; + } + if (other instanceof Node) { + return Objects.equals(type, ((Node) other).getType()); + } + if (other instanceof Entity) { + return Objects.equals(type, ((Entity) other).getType()) + && Objects.equals(id, ((Entity) other).getId()); + } + return equals(other); + } + + @Override + public String alias() { + return this.alias; + } + + @Override + public Element cleanAlias() { + return new Entity(this.id, this.type); + } + @Override public int hashCode() { return Objects.hash(id, type, alias); } @Override - public String toString() { + public Element bind(Element pattern) { + if (pattern instanceof Entity || pattern instanceof Node) { + return new Entity(this.id, this.type, pattern.alias()); + } else { + return this; + } + } + + @Override + public String shortString() { StringBuilder sb = new StringBuilder(); sb.append(type).append("/").append(id); return sb.toString(); diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Node.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Node.java index a63a7134a..9807fcfe3 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Node.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Node.java @@ -52,12 +52,37 @@ public boolean matches(Element other) { if (other == null) { return false; } + if (other instanceof Any) { + return true; + } + if (other instanceof Node) { + return Objects.equals(type, ((Node) other).getType()); + } if (other instanceof Entity) { return Objects.equals(type, ((Entity) other).getType()); } return equals(other); } + @Override + public Element bind(Element pattern) { + if (pattern instanceof Entity) { + return new Entity(((Entity) pattern).getId(), this.type, ((Entity) pattern).getAlias()); + } else { + return this; + } + } + + @Override + public String alias() { + return this.alias; + } + + @Override + public Element cleanAlias() { + return new Node(this.type); + } + /** * Getter method for property type. * diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Predicate.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Predicate.java index 2477726a1..c4edc5373 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Predicate.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Predicate.java @@ -13,6 +13,7 @@ package com.antgroup.openspg.reasoner.thinker.logic.graph; +import com.antgroup.openspg.reasoner.common.exception.UnsupportedOperationException; import java.util.Objects; import lombok.Data; @@ -84,4 +85,32 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(name, alias); } + + public boolean matches(Element other) { + if (other == null) { + return false; + } + if (other instanceof Predicate) { + return Objects.equals(name, ((Predicate) other).getName()); + } + return equals(other); + } + + public String alias() { + return alias; + } + + @Override + public Element cleanAlias() { + return new Predicate(this.name); + } + + @Override + public Element bind(Element pattern) { + if (pattern instanceof Predicate) { + return new Predicate(name, pattern.alias()); + } else { + throw new UnsupportedOperationException("Triple cannot bind " + pattern.toString(), null); + } + } } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Triple.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Triple.java index 1aa393131..61b0e62b5 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Triple.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Triple.java @@ -12,6 +12,7 @@ */ package com.antgroup.openspg.reasoner.thinker.logic.graph; +import com.antgroup.openspg.reasoner.common.exception.UnsupportedOperationException; import java.util.Objects; import lombok.Data; @@ -40,6 +41,27 @@ public boolean matches(Element other) { } } + @Override + public Element bind(Element pattern) { + if (pattern instanceof Triple) { + return new Triple( + subject.bind(((Triple) pattern).getSubject()), + predicate.bind(((Triple) pattern).getPredicate()), + object.bind(((Triple) pattern).getObject())); + } else { + throw new UnsupportedOperationException("Triple cannot bind " + pattern.toString(), null); + } + } + + public String alias() { + return predicate.alias(); + } + + @Override + public Element cleanAlias() { + return new Triple(subject.cleanAlias(), predicate.cleanAlias(), object.cleanAlias()); + } + public static Triple create(Element s, Element p, Element o) { return new Triple(nullToAny(s), nullToAny(p), nullToAny(o)); } @@ -120,4 +142,15 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(subject, predicate, object); } + + @Override + public String shortString() { + StringBuilder sb = new StringBuilder(); + sb.append(subject.alias()) + .append("_") + .append(predicate.alias()) + .append("_") + .append(object.alias()); + return sb.toString(); + } } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Value.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Value.java index 424c6148c..46f145b85 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Value.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Value.java @@ -17,13 +17,79 @@ @Data public class Value extends Element { - private String name; private Object val; + private String alias; public Value() {} - public Value(String name, Object val) { - this.name = name; + public Value(Object val) { + this.val = val; + } + + public Value(Object val, String alias) { + this.val = val; + this.alias = alias; + } + + @Override + public String alias() { + return this.alias; + } + + @Override + public Element cleanAlias() { + return new Value(val); + } + + public boolean matches(Element other) { + if (other != null && other instanceof Value) { + return true; + } + return false; + } + + @Override + public Element bind(Element pattern) { + if (pattern instanceof Value) { + return new Value(val, pattern.alias()); + } else { + return this; + } + } + + /** + * Getter method for property alias. + * + * @return property value of alias + */ + public String getAlias() { + return alias; + } + + /** + * Setter method for property alias. + * + * @param alias value to be assigned to property alias + */ + public void setAlias(String alias) { + this.alias = alias; + } + + /** + * Getter method for property val. + * + * @return property value of val + */ + public Object getVal() { + return val; + } + + /** + * Setter method for property val. + * + * @param val value to be assigned to property val + */ + public void setVal(Object val) { this.val = val; } } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/TreeLogger.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/TreeLogger.java index 77c2fd966..c6322b02a 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/TreeLogger.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/TreeLogger.java @@ -31,7 +31,7 @@ public class TreeLogger implements Serializable { private static final String V_IDENT = "\u2502 "; private String currentNodeName; - private StringBuilder currentNodeMsg; + private String currentNodeMsg; private Boolean currentNodeRst; private List children; @@ -40,13 +40,8 @@ public TreeLogger(String currentNodeName) { } public TreeLogger log(Object msg) { - if (this.currentNodeMsg == null) { - this.currentNodeMsg = new StringBuilder(); - } if (msg != null) { - this.currentNodeMsg.append(msg); - } else { - this.currentNodeMsg.append("null"); + this.currentNodeMsg = msg.toString(); } return this; } @@ -133,7 +128,7 @@ public String getCurrentNodeName() { * * @return property value of currentNodeMsg */ - public StringBuilder getCurrentNodeMsg() { + public String getCurrentNodeMsg() { return currentNodeMsg; } @@ -142,7 +137,7 @@ public StringBuilder getCurrentNodeMsg() { * * @param currentNodeMsg value to be assigned to property currentNodeMsg */ - public void setCurrentNodeMsg(StringBuilder currentNodeMsg) { + public void setCurrentNodeMsg(String currentNodeMsg) { this.currentNodeMsg = currentNodeMsg; } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/exact/QlExpressCondition.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/exact/QlExpressCondition.java index 4ffdab79f..7590b1a12 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/exact/QlExpressCondition.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/exact/QlExpressCondition.java @@ -13,29 +13,104 @@ package com.antgroup.openspg.reasoner.thinker.logic.rule.exact; -import com.antgroup.openspg.reasoner.thinker.logic.graph.Element; +import com.antgroup.openspg.reasoner.thinker.logic.graph.*; import com.antgroup.openspg.reasoner.thinker.logic.rule.TreeLogger; -import com.antgroup.openspg.reasoner.udf.rule.RuleRunner; +import com.antgroup.openspg.reasoner.thinker.qlexpress.QlExpressRunner; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; import java.util.*; +import java.util.concurrent.TimeUnit; import lombok.Data; +import org.apache.commons.collections4.CollectionUtils; @Data public class QlExpressCondition extends Condition { + private static LoadingCache>> varsCache; private String qlExpress; public QlExpressCondition(String qlExpress) { this.qlExpress = qlExpress; } + static { + synchronized (QlExpressCondition.class) { + if (null == varsCache) { + LoadingCache>> tmpCache = + CacheBuilder.newBuilder() + .concurrencyLevel(8) + .maximumSize(100) + .expireAfterAccess(30, TimeUnit.MINUTES) + .build( + new CacheLoader>>() { + @Override + public Map> load(String rule) throws Exception { + try { + return ((QlExpressRunner) QlExpressRunner.getInstance()) + .getParamNames(rule); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + }); + varsCache = tmpCache; + } + } + } + @Override public Boolean execute(List spoList, Map context, TreeLogger logger) { Map ruleCtx = new HashMap<>(); - ruleCtx.putAll(context); + if (context != null) { + ruleCtx.putAll(context); + } for (Element element : spoList) { - ruleCtx.put(element.toString(), true); + ruleCtx.put(element.shortString(), true); + if (element instanceof Triple) { + Triple triple = (Triple) element; + if (triple.getObject() instanceof Value) { + Map props = + (Map) + ruleCtx.computeIfAbsent( + triple.getSubject().alias(), (k) -> new HashMap()); + props.put( + ((Predicate) ((Triple) element).getPredicate()).getName(), + ((Value) triple.getObject()).getVal()); + } + } + } + try { + boolean absent = absent(ruleCtx); + if (absent) { + return null; + } + Object rst = + QlExpressRunner.getInstance().executeExpression(ruleCtx, Arrays.asList(qlExpress), ""); + if (rst == null) { + return false; + } else { + return (Boolean) rst; + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private boolean absent(Map context) throws Exception { + if (qlExpress.toLowerCase().contains("get_value") + || qlExpress.toLowerCase().contains("get_spo")) { + return false; + } + Map> vars = varsCache.get(qlExpress); + for (String key : vars.keySet()) { + if (CollectionUtils.isNotEmpty(vars.get(key))) { + continue; + } + if (!context.containsKey(key)) { + return true; + } } - Object rst = RuleRunner.getInstance().executeExpression(ruleCtx, Arrays.asList(qlExpress), ""); - return (Boolean) rst; + return false; } @Override diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/visitor/RuleExecutor.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/visitor/RuleExecutor.java index 409c50a7f..f30d2f38f 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/visitor/RuleExecutor.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/visitor/RuleExecutor.java @@ -31,7 +31,7 @@ public Boolean visit( Boolean ret = null; for (Node child : node.getChildren()) { Boolean c = child.accept(spoList, context, this, logger.addChild(child.toString())); - c = c == null ? false : c; + c = c == null ? true : c; if (ret == null) { ret = c; } else { @@ -49,7 +49,7 @@ public Boolean visit( Boolean ret = null; for (Node child : node.getChildren()) { Boolean c = child.accept(spoList, context, this, logger.addChild(child.toString())); - c = c == null ? false : c; + c = c == null ? true : c; if (ret == null) { ret = c; } else { @@ -67,7 +67,7 @@ public Boolean visit( Boolean ret = null; Node child = node.getChild(); Boolean r = child.accept(spoList, context, this, logger); - r = r == null ? false : r; + r = r == null ? true : r; ret = !r; logger.log(ret); logger.setCurrentNodeRst(ret); @@ -78,9 +78,8 @@ public Boolean visit( public Boolean visit( Condition node, List spoList, Map context, TreeLogger logger) { Boolean ret = node.execute(spoList, context, logger); - ret = ret == null ? false : ret; logger.log(ret); logger.setCurrentNodeRst(ret); - return ret; + return ret == null ? true : ret; } } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/qlexpress/QlExpressRunner.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/qlexpress/QlExpressRunner.java new file mode 100644 index 000000000..99faf5078 --- /dev/null +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/qlexpress/QlExpressRunner.java @@ -0,0 +1,121 @@ +/* + * 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.thinker.qlexpress; + +import com.antgroup.openspg.reasoner.thinker.qlexpress.op.RichOperatorEqualsLessMore; +import com.antgroup.openspg.reasoner.udf.rule.RuleRunner; +import com.google.common.collect.Lists; +import com.ql.util.express.InstructionSet; +import com.ql.util.express.Operator; +import com.ql.util.express.instruction.detail.Instruction; +import com.ql.util.express.instruction.detail.InstructionConstData; +import com.ql.util.express.instruction.detail.InstructionLoadAttr; +import com.ql.util.express.instruction.detail.InstructionOperator; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import scala.Tuple2; + +public class QlExpressRunner extends RuleRunner { + private static volatile QlExpressRunner instance = null; + + private QlExpressRunner() {} + + public static QlExpressRunner getInstance() { + if (null != instance) { + return instance; + } + synchronized (QlExpressRunner.class) { + if (instance == null) { + QlExpressRunner runner = new QlExpressRunner(); + runner.init(); + instance = runner; + } + } + return instance; + } + + @Override + protected void init() { + super.init(); + overrideOperator(); + } + + public Map> getParamNames(String rule) throws Exception { + InstructionSet instructionSet = EXPRESS_RUNNER.getInstructionSetFromLocalCache(rule); + Map> result = new TreeMap<>(); + for (int i = 0; i < instructionSet.getInstructionLength(); i++) { + Instruction instruction = instructionSet.getInstruction(i); + if (instruction instanceof InstructionLoadAttr) { + if ("null".equals(((InstructionLoadAttr) instruction).getAttrName())) { + continue; + } + String aliasName = ((InstructionLoadAttr) instruction).getAttrName(); + Set varSet = result.computeIfAbsent(aliasName, (k) -> new HashSet<>()); + // LoadAttr之后就是具体的属性名 + Instruction next = instructionSet.getInstruction(i + 1); + if (next instanceof InstructionOperator) { + String opName = ((InstructionOperator) next).getOperator().getName(); + if ("FieldCall".equalsIgnoreCase(opName)) { + String varName = ((InstructionOperator) next).getOperator().toString().split(":")[1]; + varSet.add(varName); + } + } + } + } + + for (int i = 0; i < instructionSet.getInstructionLength(); i++) { + Instruction instruction = instructionSet.getInstruction(i); + if (instruction instanceof InstructionOperator) { + String opName = ((InstructionOperator) instruction).getOperator().getName(); + if (opName != null) { + if (opName.equalsIgnoreCase("def") || opName.equalsIgnoreCase("exportDef")) { + if (i >= 1) { + String varLocalName = + (String) + ((InstructionConstData) instructionSet.getInstruction(i - 1)) + .getOperateData() + .getObject(null); + result.remove(varLocalName); + } + } else if (opName.equalsIgnoreCase("alias") || opName.equalsIgnoreCase("exportAlias")) { + if (i >= 2) { + String varLocalName = + (String) + ((InstructionConstData) instructionSet.getInstruction(i - 2)) + .getOperateData() + .getObject(null); + result.remove(varLocalName); + } + } + } + } + } + return result; + } + + @Override + protected void overrideOperator() { + Lists.newArrayList( + new Tuple2("<", new RichOperatorEqualsLessMore("<")), + new Tuple2(">", new RichOperatorEqualsLessMore(">")), + new Tuple2("<=", new RichOperatorEqualsLessMore("<=")), + new Tuple2(">=", new RichOperatorEqualsLessMore(">=")), + new Tuple2("==", new RichOperatorEqualsLessMore("==")), + new Tuple2("!=", new RichOperatorEqualsLessMore("!=")), + new Tuple2("<>", new RichOperatorEqualsLessMore("<>"))) + .forEach(udfTuple -> EXPRESS_RUNNER.replaceOperator(udfTuple._1(), udfTuple._2())); + } +} diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/qlexpress/op/RichOperatorEqualsLessMore.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/qlexpress/op/RichOperatorEqualsLessMore.java new file mode 100644 index 000000000..2623ea532 --- /dev/null +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/qlexpress/op/RichOperatorEqualsLessMore.java @@ -0,0 +1,144 @@ +/* + * 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.thinker.qlexpress.op; + +import com.antgroup.openspg.reasoner.udf.rule.op.OperatorEqualsLessMore; +import java.math.BigDecimal; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.apache.commons.lang3.math.NumberUtils; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; + +/** + * Custom comparison operator logic: + * + *

1. When comparing strings with numerical types, convert the string to a number. 2. Support + * evaluation of expressions of the type ">150" > 150. + */ +public class RichOperatorEqualsLessMore extends OperatorEqualsLessMore { + private static final String reg = "[<>]?\\s*\\d+(\\.\\d+)?"; + private Pattern pattern; + + public RichOperatorEqualsLessMore(String aName) { + super(aName); + pattern = Pattern.compile(reg); + } + + public RichOperatorEqualsLessMore(String aAliasName, String aName, String aErrorInfo) { + super(aAliasName, aName, aErrorInfo); + pattern = Pattern.compile(reg); + } + + @Override + public Object executeInner(Object[] list) throws Exception { + return executeInner(list[0], list[1]); + } + + @Override + public Object executeInner(Object op1, Object op2) { + if (op1 instanceof Number && op2 instanceof String) { + String value = getValue((String) op2); + if (value == null) { + return null; + } else if (NumberUtils.isCreatable(value)) { + return executeInner(this.name, op1, toNumber((String) op2, op1.getClass())); + } else { + Number num = toNumber(value.substring(1, value.length()), op1.getClass()); + Pair right = buildRange(value.substring(0, 1), num); + Pair left = buildRange(this.name, (Number) op1); + if (left == null || right == null) { + return OperatorEqualsLessMore.executeInner(this.name, op1, op2); + } else { + return executeInner(left, right); + } + } + } else if (op1 instanceof String && op2 instanceof Number) { + String value = getValue((String) op1); + if (value == null) { + return null; + } else if (NumberUtils.isCreatable(value)) { + return executeInner(this.name, toNumber((String) op1, op2.getClass()), op2); + } else { + Number num = toNumber(value.substring(1, value.length()), op2.getClass()); + Pair right = buildRange(value.substring(0, 1), num); + Pair left = buildRange(this.name, (Number) op2); + if (left == null || right == null) { + return OperatorEqualsLessMore.executeInner(this.name, op1, op2); + } else { + return executeInner(left, right); + } + } + } else { + return OperatorEqualsLessMore.executeInner(this.name, op1, op2); + } + } + + private Pair buildRange(String opName, Number op) { + if (">=".equals(opName) || ">".equals(opName)) { + return new ImmutablePair<>(op, Long.MAX_VALUE); + } else if ("<=".equals(opName) || "<".equals(opName)) { + return new ImmutablePair<>(Long.MIN_VALUE, op); + } else { + return null; + } + } + + private Boolean executeInner(Pair left, Pair right) { + if (OperatorEqualsLessMore.executeInner(">", left.getLeft(), right.getRight()) + || OperatorEqualsLessMore.executeInner("<", left.getRight(), right.getLeft())) { + return false; + } else if (OperatorEqualsLessMore.executeInner("<=", left.getLeft(), right.getLeft()) + && OperatorEqualsLessMore.executeInner(">=", left.getRight(), right.getRight())) { + return true; + } else { + return null; + } + } + + private String getValue(String content) { + Matcher matcher = pattern.matcher(content); + if (matcher.find()) { + return matcher.group(); + } else { + return null; + } + } + + private Number toNumber(String op, Class numberClass) { + int type = getSeq(numberClass); + if (NUMBER_TYPE_BYTE == type) { + return Byte.valueOf(op); + } + if (NUMBER_TYPE_SHORT == type) { + return Short.valueOf(op); + } + if (NUMBER_TYPE_INT == type) { + return Integer.valueOf(op); + } + if (NUMBER_TYPE_LONG == type) { + return Long.valueOf(op); + } + if (NUMBER_TYPE_FLOAT == type) { + return Float.valueOf(op); + } + if (NUMBER_TYPE_DOUBLE == type) { + return Double.valueOf(op); + } + if (NUMBER_TYPE_DECIMAL == type) { + return new BigDecimal(op); + } + return null; + } +} diff --git a/reasoner/thinker/src/main/scala/com/antgroup/openspg/reasoner/thinker/ThinkerRuleParser.scala b/reasoner/thinker/src/main/scala/com/antgroup/openspg/reasoner/thinker/ThinkerRuleParser.scala index 146cdffa2..f3a28e0e5 100644 --- a/reasoner/thinker/src/main/scala/com/antgroup/openspg/reasoner/thinker/ThinkerRuleParser.scala +++ b/reasoner/thinker/src/main/scala/com/antgroup/openspg/reasoner/thinker/ThinkerRuleParser.scala @@ -30,7 +30,7 @@ import com.antgroup.openspg.reasoner.lube.common.expr.{ } import com.antgroup.openspg.reasoner.lube.utils.transformer.impl.Expr2QlexpressTransformer import com.antgroup.openspg.reasoner.parser.expr.RuleExprParser -import com.antgroup.openspg.reasoner.thinker.logic.graph.{Element, Entity, Predicate} +import com.antgroup.openspg.reasoner.thinker.logic.graph.{Element, Entity, Predicate, Value} import com.antgroup.openspg.reasoner.thinker.logic.graph import com.antgroup.openspg.reasoner.thinker.logic.rule.{ ClauseEntry, @@ -51,6 +51,10 @@ class ThinkerRuleParser extends RuleExprParser { val expr2StringTransformer = new Expr2QlexpressTransformer() var defaultAliasNum = 0 val aliasToElementMap = new mutable.HashMap[String, Element]() + val spoRuleToSpoSetMap = new mutable.HashMap[String, (Element, Element, Element)]() + + val valueTypeSet: Set[String] = + Set.apply("STRING", "LONG", "INT", "INTEGER", "DOUBLE", "FLOAT", "BOOLEAN") val conditionToElementMap: mutable.HashMap[Condition, mutable.HashSet[ClauseEntry]] = new mutable.HashMap() @@ -129,6 +133,7 @@ class ThinkerRuleParser extends RuleExprParser { case e: Entity => e.getAlias case p: Predicate => p.getAlias case n: logic.graph.Node => n.getAlias + case v: Value => v.getAlias case _ => throw new IllegalArgumentException("%s element has no alias".format(element)) } } @@ -273,7 +278,7 @@ class ThinkerRuleParser extends RuleExprParser { val propertyName: String = propertyNameList.head.getText val subject = aliasToElementMap(alias) val predicate = new Predicate(propertyName) - val o = new graph.Any() + val o = new Value(null, "anonymous_" + getDefaultAliasNum) body += new TriplePattern(new logic.graph.Triple(subject, predicate, o)) } } @@ -314,10 +319,16 @@ class ThinkerRuleParser extends RuleExprParser { } def parseSpoRule(ctx: Spo_ruleContext, isHead: Boolean = false): (Element, Element, Element) = { + val spoRuleText = ctx.getText + if (spoRuleToSpoSetMap.contains(spoRuleText)) { + return spoRuleToSpoSetMap(spoRuleText) + } val sNode: Element = parseNode(ctx, 0, isHead) val pPredicate: Element = parsePredicate(ctx) val oNode: Element = parseNode(ctx, 1) - (sNode, pPredicate, oNode) + val tmpResult = (sNode, pPredicate, oNode) + spoRuleToSpoSetMap += (spoRuleText -> tmpResult) + tmpResult } def parseNode(ctx: Spo_ruleContext, index: Int, isHead: Boolean = false): Element = { @@ -339,6 +350,8 @@ class ThinkerRuleParser extends RuleExprParser { val conceptEntity = constructConceptEntity(conceptContext) conceptEntity.setAlias(sAlias) sNode = conceptEntity + } else if (valueTypeSet.contains(sType.toUpperCase())) { + sNode = new Value(null, sAlias) } } aliasToElementMap += (sAlias -> sNode) diff --git a/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/DefaultThinkerTests.java b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/DefaultThinkerTests.java index 45221ede2..d2e910316 100644 --- a/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/DefaultThinkerTests.java +++ b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/DefaultThinkerTests.java @@ -13,20 +13,17 @@ package com.antgroup.openspg.reasoner.thinker; -import com.antgroup.openspg.reasoner.common.constants.Constants; -import com.antgroup.openspg.reasoner.common.graph.edge.Direction; import com.antgroup.openspg.reasoner.common.graph.edge.impl.Edge; import com.antgroup.openspg.reasoner.common.graph.property.IProperty; -import com.antgroup.openspg.reasoner.common.graph.property.impl.VertexVersionProperty; import com.antgroup.openspg.reasoner.common.graph.vertex.IVertexId; import com.antgroup.openspg.reasoner.common.graph.vertex.impl.Vertex; import com.antgroup.openspg.reasoner.graphstate.GraphState; -import com.antgroup.openspg.reasoner.graphstate.impl.MemGraphState; import com.antgroup.openspg.reasoner.thinker.catalog.MockLogicCatalog; import com.antgroup.openspg.reasoner.thinker.engine.DefaultThinker; import com.antgroup.openspg.reasoner.thinker.logic.Result; import com.antgroup.openspg.reasoner.thinker.logic.graph.Entity; import com.antgroup.openspg.reasoner.thinker.logic.graph.Node; +import com.antgroup.openspg.reasoner.thinker.logic.graph.Predicate; import com.antgroup.openspg.reasoner.thinker.logic.rule.Rule; import java.util.*; import org.junit.Assert; @@ -34,79 +31,16 @@ public class DefaultThinkerTests { private GraphState buildGraphState() { - GraphState graphState = new MemGraphState(); + Vertex a1 = GraphUtil.makeVertex("a1", "A"); + Vertex a2 = GraphUtil.makeVertex("a2", "A"); + Vertex b = GraphUtil.makeVertex("b", "B"); + Vertex c = GraphUtil.makeVertex("c", "C"); - Vertex vertex1 = - new Vertex<>(IVertexId.from("a1", "A"), new VertexVersionProperty()); - Vertex vertex2 = - new Vertex<>(IVertexId.from("a2", "A"), new VertexVersionProperty()); - Vertex vertex3 = - new Vertex<>(IVertexId.from("b", "B"), new VertexVersionProperty()); - Vertex vertex4 = - new Vertex<>(IVertexId.from("c", "C"), new VertexVersionProperty("k1", "abcd")); + Edge a1b = GraphUtil.makeEdge(a1, b, "ab"); + Edge a2b = GraphUtil.makeEdge(a2, b, "ab"); + Edge bc = GraphUtil.makeEdge(b, c, "bc"); - Edge a1b = - new Edge( - IVertexId.from("a1", "A"), - IVertexId.from("b", "B"), - new VertexVersionProperty( - Constants.EDGE_FROM_ID_TYPE_KEY, - "A", - Constants.EDGE_TO_ID_TYPE_KEY, - "B", - Constants.EDGE_FROM_ID_KEY, - "a1", - Constants.EDGE_TO_ID_KEY, - "b"), - 0L, - Direction.OUT, - "ab"); - Edge a2b = - new Edge( - IVertexId.from("a2", "A"), - IVertexId.from("b", "B"), - new VertexVersionProperty( - Constants.EDGE_FROM_ID_TYPE_KEY, - "A", - Constants.EDGE_TO_ID_TYPE_KEY, - "B", - Constants.EDGE_FROM_ID_KEY, - "a1", - Constants.EDGE_TO_ID_KEY, - "b"), - 0L, - Direction.OUT, - "ab"); - Edge bc = - new Edge( - IVertexId.from("b", "B"), - IVertexId.from("c", "C"), - new VertexVersionProperty( - Constants.EDGE_FROM_ID_TYPE_KEY, - "B", - Constants.EDGE_TO_ID_TYPE_KEY, - "C", - Constants.EDGE_FROM_ID_KEY, - "b", - Constants.EDGE_TO_ID_KEY, - "c"), - 0L, - Direction.OUT, - "bc"); - - graphState.addVertex(vertex1); - graphState.addEdges(vertex1.getId(), new LinkedList<>(), Arrays.asList(a1b)); - graphState.addVertex(vertex2); - graphState.addEdges(vertex2.getId(), new LinkedList<>(), Arrays.asList(a2b)); - - graphState.addVertex(vertex3); - graphState.addEdges( - vertex3.getId(), Arrays.asList(a1b.reverse(), a2b.reverse()), Arrays.asList(bc)); - - graphState.addVertex(vertex4); - graphState.addEdges(vertex4.getId(), Arrays.asList(bc.reverse()), new LinkedList<>()); - - return graphState; + return GraphUtil.buildMemState(Arrays.asList(a1, a2, b, c), Arrays.asList(a1b, a2b, bc)); } @Test @@ -115,7 +49,7 @@ public void testFindForward() { mockLogicCatalog.init(); Thinker thinker = new DefaultThinker(buildGraphState(), mockLogicCatalog); List triples = thinker.find(new Entity("a1", "A"), null, null); - Assert.assertTrue(triples.size() == 1); + Assert.assertTrue(triples.size() == 2); } @Test @@ -133,8 +67,18 @@ private Rule getR1() { return parser.parseSimplifyDsl(rule, null).head(); } + private Rule getR2() { + String rule = + "Define (a:A)-[:ac]->(c:C) {\n" + + " R1: (a)-[: ab]->(b: B) AND (b)-[:bc]->(c:C) \n" + + "}\n" + + "Description: \"(a, ab, b), (b, bc, c) -> (a, ac, c)\""; + SimplifyThinkerParser parser = new SimplifyThinkerParser(); + return parser.parseSimplifyDsl(rule, null).head(); + } + @Test - public void testFindWithRule() { + public void testFindWithRule1() { MockLogicCatalog logicCatalog = new MockLogicCatalog(Arrays.asList(getR1())); logicCatalog.init(); Thinker thinker = new DefaultThinker(buildGraphState(), logicCatalog); @@ -146,4 +90,15 @@ public void testFindWithRule() { List triples = thinker.find(new Node("D"), context); Assert.assertTrue(triples.size() == 1); } + + @Test + public void testFindWithRule2() { + MockLogicCatalog logicCatalog = new MockLogicCatalog(Arrays.asList(getR2())); + logicCatalog.init(); + Thinker thinker = new DefaultThinker(buildGraphState(), logicCatalog); + List triples = thinker.find(new Entity("a1", "A"), new Predicate("ac"), new Node("C")); + Assert.assertTrue(triples.size() == 1); + triples = thinker.find(new Node("A"), new Predicate("ac"), new Entity("c", "C")); + Assert.assertTrue(triples.size() == 2); + } } diff --git a/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/GraphUtil.java b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/GraphUtil.java new file mode 100644 index 000000000..b82eeaa42 --- /dev/null +++ b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/GraphUtil.java @@ -0,0 +1,81 @@ +/* + * 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.thinker; + +import com.antgroup.openspg.reasoner.common.Utils; +import com.antgroup.openspg.reasoner.common.constants.Constants; +import com.antgroup.openspg.reasoner.common.graph.edge.Direction; +import com.antgroup.openspg.reasoner.common.graph.edge.IEdge; +import com.antgroup.openspg.reasoner.common.graph.edge.impl.Edge; +import com.antgroup.openspg.reasoner.common.graph.property.IProperty; +import com.antgroup.openspg.reasoner.common.graph.property.IVersionProperty; +import com.antgroup.openspg.reasoner.common.graph.property.impl.VertexVersionProperty; +import com.antgroup.openspg.reasoner.common.graph.vertex.IVertex; +import com.antgroup.openspg.reasoner.common.graph.vertex.IVertexId; +import com.antgroup.openspg.reasoner.common.graph.vertex.impl.Vertex; +import com.antgroup.openspg.reasoner.graphstate.impl.MemGraphState; +import java.util.*; + +public class GraphUtil { + public static Edge makeEdge( + Vertex from, + Vertex to, + String edgeType, + Object... kvs) { + Map> props = new HashMap<>(); + Map propertyMap = Utils.convert2Property(kvs); + propertyMap.put(Constants.EDGE_FROM_ID_TYPE_KEY, from.getId().getType()); + propertyMap.put(Constants.EDGE_TO_ID_TYPE_KEY, to.getId().getType()); + propertyMap.put(Constants.EDGE_FROM_ID_KEY, from.getValue().get(Constants.NODE_ID_KEY)); + propertyMap.put(Constants.EDGE_TO_ID_KEY, to.getValue().get(Constants.NODE_ID_KEY)); + for (Map.Entry entry : propertyMap.entrySet()) { + TreeMap valueMap = new TreeMap<>(); + valueMap.put(0L, entry.getValue()); + props.put(entry.getKey(), valueMap); + } + return new Edge<>( + from.getId(), to.getId(), new VertexVersionProperty(props), 0L, Direction.OUT, edgeType); + } + + public static Vertex makeVertex(String id, String type, Object... kvs) { + Map> props = new HashMap<>(); + Map propertyMap = Utils.convert2Property(kvs); + propertyMap.put(Constants.NODE_ID_KEY, id); + for (Map.Entry entry : propertyMap.entrySet()) { + TreeMap valueMap = new TreeMap<>(); + valueMap.put(0L, entry.getValue()); + props.put(entry.getKey(), valueMap); + } + return new Vertex<>(IVertexId.from(id, type), new VertexVersionProperty(props)); + } + + public static MemGraphState buildMemState( + List> vertexList, List> edgeList) { + MemGraphState graphState = new MemGraphState(); + for (IVertex v : vertexList) { + graphState.addVertex(v); + List> outEdges = new LinkedList<>(); + List> inEdges = new LinkedList<>(); + for (IEdge e : edgeList) { + if (v.getId().equals(e.getSourceId())) { + outEdges.add(e); + } else if (v.getId().equals(e.getTargetId())) { + inEdges.add(e.reverse()); + } + } + graphState.addEdges(v.getId(), inEdges, outEdges); + } + return graphState; + } +} diff --git a/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/HypertensionTest.java b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/HypertensionTest.java new file mode 100644 index 000000000..0f82cd851 --- /dev/null +++ b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/HypertensionTest.java @@ -0,0 +1,73 @@ +/* + * 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.thinker; + +import com.antgroup.openspg.reasoner.common.graph.vertex.IVertexId; +import com.antgroup.openspg.reasoner.graphstate.GraphState; +import com.antgroup.openspg.reasoner.graphstate.impl.MemGraphState; +import com.antgroup.openspg.reasoner.thinker.catalog.ResourceLogicCatalog; +import com.antgroup.openspg.reasoner.thinker.engine.DefaultThinker; +import com.antgroup.openspg.reasoner.thinker.logic.Result; +import com.antgroup.openspg.reasoner.thinker.logic.graph.Node; +import com.antgroup.openspg.reasoner.thinker.logic.graph.Predicate; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.Assert; +import org.junit.Test; + +public class HypertensionTest { + private GraphState buildGraphState() { + GraphState graphState = new MemGraphState(); + return graphState; + } + + private Thinker buildThinker() { + ResourceLogicCatalog logicCatalog = new ResourceLogicCatalog("/Hypertension.txt"); + logicCatalog.init(); + Thinker thinker = new DefaultThinker(buildGraphState(), logicCatalog); + return thinker; + } + + @Test + public void bloodPressureLevel() { + Thinker thinker = buildThinker(); + Map context = new HashMap<>(); + context.put("收缩压", 160); + List triples = thinker.find(new Node("血压水平分级"), context); + Assert.assertTrue(triples.size() == 3); + } + + @Test + public void hypertensionLevel() { + Thinker thinker = buildThinker(); + Map context = new HashMap<>(); + context.put("BMI", 35); + context.put("GFR", 35); + List triples = thinker.find(new Node("高血压分层"), context); + Assert.assertTrue(triples.size() == 2); + } + + @Test + public void combinationDrug() { + Thinker thinker = buildThinker(); + Map context = new HashMap<>(); + context.put("收缩压", 160); + context.put("目标收缩压上界", 140); + context.put("BMI", 35); + context.put("GFR", 35); + List triples = thinker.find(null, new Predicate("基本用药方案"), new Node("药品"), context); + Assert.assertTrue(triples.size() == 2); + } +} diff --git a/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/InsuranceTests.java b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/InsuranceTests.java new file mode 100644 index 000000000..d30a98436 --- /dev/null +++ b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/InsuranceTests.java @@ -0,0 +1,124 @@ +/* + * 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.thinker; + +import com.antgroup.openspg.reasoner.common.graph.edge.IEdge; +import com.antgroup.openspg.reasoner.common.graph.edge.impl.Edge; +import com.antgroup.openspg.reasoner.common.graph.property.IProperty; +import com.antgroup.openspg.reasoner.common.graph.vertex.IVertex; +import com.antgroup.openspg.reasoner.common.graph.vertex.IVertexId; +import com.antgroup.openspg.reasoner.common.graph.vertex.impl.Vertex; +import com.antgroup.openspg.reasoner.graphstate.GraphState; +import com.antgroup.openspg.reasoner.thinker.catalog.ResourceLogicCatalog; +import com.antgroup.openspg.reasoner.thinker.engine.DefaultThinker; +import com.antgroup.openspg.reasoner.thinker.logic.Result; +import com.antgroup.openspg.reasoner.thinker.logic.graph.Entity; +import com.antgroup.openspg.reasoner.thinker.logic.graph.Node; +import com.antgroup.openspg.reasoner.thinker.logic.graph.Predicate; +import java.util.*; +import org.junit.Assert; +import org.junit.Test; + +public class InsuranceTests { + + private GraphState buildGraphState() { + Vertex v1 = GraphUtil.makeVertex("肺钙化", "InsDisease"); + Vertex v2 = GraphUtil.makeVertex("肺部肿物或结节", "InsDisease"); + Vertex v3 = GraphUtil.makeVertex("肺炎性假瘤", "InsDisease"); + Vertex v4 = GraphUtil.makeVertex("肺病", "InsDisease"); + Vertex v5 = GraphUtil.makeVertex("肺癌", "InsDisease"); + Vertex v6 = GraphUtil.makeVertex("既往症", "InsDiseaseDisclaim"); + Vertex v7 = GraphUtil.makeVertex("好医保0免赔", "InsClause"); + Vertex v8 = GraphUtil.makeVertex("好医保0免赔", "InsComProd"); + + Edge e1 = GraphUtil.makeEdge(v1, v2, "child"); + Edge e2 = GraphUtil.makeEdge(v2, v3, "child"); + Edge e3 = GraphUtil.makeEdge(v4, v3, "child"); + Edge e4 = GraphUtil.makeEdge(v4, v5, "child"); + Edge e5 = GraphUtil.makeEdge(v1, v5, "child"); + + Edge e6 = GraphUtil.makeEdge(v2, v2, "evolve"); + Edge e7 = GraphUtil.makeEdge(v5, v5, "evolve"); + Edge e8 = GraphUtil.makeEdge(v3, v2, "evolve"); + + Edge e9 = GraphUtil.makeEdge(v2, v6, "disclaimClause", "disclaimType", "既往"); + Edge e10 = GraphUtil.makeEdge(v6, v7, "clauseVersion"); + Edge e11 = GraphUtil.makeEdge(v7, v8, "insClauseVersion"); + + List> vertexList = Arrays.asList(v1, v2, v3, v4, v5, v6, v7, v8); + List> edgeList = + Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11); + return GraphUtil.buildMemState(vertexList, edgeList); + } + + @Test + public void directEvolve() { + ResourceLogicCatalog logicCatalog = new ResourceLogicCatalog("/InsuranceRules.txt"); + logicCatalog.init(); + Thinker thinker = new DefaultThinker(buildGraphState(), logicCatalog); + + List triples = + thinker.find( + new Entity("肺钙化", "InsDisease"), + new Predicate("directEvolve"), + new Entity("肺病", "InsDisease"), + null); + Assert.assertTrue(triples.size() == 0); + } + + @Test + public void inDirectEvolveForward() { + ResourceLogicCatalog logicCatalog = new ResourceLogicCatalog("/InsuranceRules.txt"); + logicCatalog.init(); + Thinker thinker = new DefaultThinker(buildGraphState(), logicCatalog); + + List triples = + thinker.find( + new Entity("肺钙化", "InsDisease"), + new Predicate("inDirectEvolve"), + new Node("InsDisease"), + new HashMap<>()); + Assert.assertTrue(triples.size() == 3); + } + + @Test + public void childDisease() { + ResourceLogicCatalog logicCatalog = new ResourceLogicCatalog("/InsuranceRules.txt"); + logicCatalog.init(); + Thinker thinker = new DefaultThinker(buildGraphState(), logicCatalog); + + List triples = + thinker.find( + new Entity("肺病", "InsDisease"), + new Predicate("inDirectEvolve"), + new Node("InsDisease"), + new HashMap<>()); + Assert.assertTrue(triples.size() == 2); + } + + @Test + public void disclaim() { + ResourceLogicCatalog logicCatalog = new ResourceLogicCatalog("/InsuranceRules.txt"); + logicCatalog.init(); + Thinker thinker = new DefaultThinker(buildGraphState(), logicCatalog); + + List triples = + thinker.find( + new Entity("肺部肿物或结节", "InsDisease"), + new Predicate("disclaim"), + new Entity("好医保0免赔", "InsComProd"), + new HashMap<>()); + Assert.assertTrue(triples.size() == 1); + } +} diff --git a/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/MedTests.java b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/MedTests.java new file mode 100644 index 000000000..9a5d9023b --- /dev/null +++ b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/MedTests.java @@ -0,0 +1,90 @@ +/* + * 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.thinker; + +import com.antgroup.openspg.reasoner.common.graph.property.IProperty; +import com.antgroup.openspg.reasoner.common.graph.property.impl.VertexVersionProperty; +import com.antgroup.openspg.reasoner.common.graph.vertex.IVertexId; +import com.antgroup.openspg.reasoner.common.graph.vertex.impl.Vertex; +import com.antgroup.openspg.reasoner.graphstate.GraphState; +import com.antgroup.openspg.reasoner.graphstate.impl.MemGraphState; +import com.antgroup.openspg.reasoner.thinker.catalog.ResourceLogicCatalog; +import com.antgroup.openspg.reasoner.thinker.engine.DefaultThinker; +import com.antgroup.openspg.reasoner.thinker.logic.Result; +import com.antgroup.openspg.reasoner.thinker.logic.graph.Entity; +import com.antgroup.openspg.reasoner.thinker.logic.graph.Predicate; +import com.antgroup.openspg.reasoner.thinker.logic.graph.Value; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.Assert; +import org.junit.Test; + +public class MedTests { + private GraphState buildGraphState() { + GraphState graphState = new MemGraphState(); + + Vertex vertex1 = + new Vertex<>( + IVertexId.from("尿酸", "Med.Examination"), + new VertexVersionProperty( + "highExplain", "highExplain desc", "lowExplain", "lowExplain desc")); + graphState.addVertex(vertex1); + + return graphState; + } + + @Test + public void test() { + ResourceLogicCatalog logicCatalog = new ResourceLogicCatalog("/Medical.txt"); + logicCatalog.init(); + Thinker thinker = new DefaultThinker(buildGraphState(), logicCatalog); + // test for normal + Map context = new HashMap<>(); + context.put("population", "男性"); + context.put("value", 460); + List triples = + thinker.find( + new Entity("尿酸", "Med.Examination"), + new Predicate("abnormalRule"), + new Value(), + context); + Assert.assertTrue(triples.size() == 1); + + // test for absent + triples = + thinker.find( + new Entity("尿酸", "Med.Examination"), + new Predicate("abnormalRule"), + new Value(), + new HashMap<>()); + Assert.assertTrue(triples.size() == 4); + } + + @Test + public void testHigh() { + ResourceLogicCatalog logicCatalog = new ResourceLogicCatalog("/Medical.txt"); + logicCatalog.init(); + Thinker thinker = new DefaultThinker(buildGraphState(), logicCatalog); + Map context = new HashMap<>(); + context.put("value", "阳性"); + List triples = + thinker.find( + new Entity("尿酸", "Med.Examination"), + new Predicate("abnormalRule"), + new Value(), + context); + Assert.assertTrue(triples.size() == 2); + } +} diff --git a/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/catalog/ResourceLogicCatalog.java b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/catalog/ResourceLogicCatalog.java new file mode 100644 index 000000000..87cd3a81d --- /dev/null +++ b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/catalog/ResourceLogicCatalog.java @@ -0,0 +1,90 @@ +/* + * 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.thinker.catalog; + +import com.antgroup.openspg.reasoner.lube.catalog.AbstractConnection; +import com.antgroup.openspg.reasoner.lube.catalog.SemanticPropertyGraph; +import com.antgroup.openspg.reasoner.lube.catalog.struct.Field; +import com.antgroup.openspg.reasoner.thinker.SimplifyThinkerParser; +import com.antgroup.openspg.reasoner.thinker.logic.LogicNetwork; +import com.antgroup.openspg.reasoner.thinker.logic.rule.Rule; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.LinkedList; +import java.util.List; +import org.apache.commons.lang3.StringUtils; +import scala.collection.immutable.Map; +import scala.collection.immutable.Set; + +public class ResourceLogicCatalog extends LogicCatalog { + private SimplifyThinkerParser parser; + private String path; + + public ResourceLogicCatalog(String path) { + this.path = path; + this.parser = new SimplifyThinkerParser(); + } + + @Override + public LogicNetwork loadLogicNetwork() { + InputStream inputStream = this.getClass().getResourceAsStream(path); + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); + StringBuilder sb = new StringBuilder(); + List rules = new LinkedList<>(); + try { + String line = null; + while ((line = reader.readLine()) != null) { + if (StringUtils.isNotBlank(line)) { + sb.append(line).append("\n"); + } else { + rules.add(sb.toString()); + sb = new StringBuilder(); + } + } + } catch (IOException ex) { + throw new RuntimeException(ex); + } + if (StringUtils.isNotBlank(sb)) { + rules.add(sb.toString()); + } + LogicNetwork logicNetwork = new LogicNetwork(); + for (String r : rules) { + Rule rule = parser.parseSimplifyDsl(r, null).head(); + logicNetwork.addRule(rule); + } + return logicNetwork; + } + + @Override + public SemanticPropertyGraph getKnowledgeGraph() { + return null; + } + + @Override + public Map> getConnections() { + return null; + } + + @Override + public Set getDefaultNodeProperties() { + return null; + } + + @Override + public Set getDefaultEdgeProperties() { + return null; + } +} diff --git a/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/logic/rule/QlExpressTest.java b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/logic/rule/QlExpressTest.java new file mode 100644 index 000000000..40e6fd597 --- /dev/null +++ b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/logic/rule/QlExpressTest.java @@ -0,0 +1,83 @@ +/* + * 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.thinker.logic.rule; + +import com.antgroup.openspg.reasoner.thinker.qlexpress.QlExpressRunner; +import com.antgroup.openspg.reasoner.udf.rule.RuleRunner; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import org.junit.Assert; +import org.junit.Test; + +public class QlExpressTest { + @Test + public void testNumberCompString() { + RuleRunner runner = QlExpressRunner.getInstance(); + Map context = new HashMap<>(); + context.put("value", "160"); + Assert.assertTrue((Boolean) runner.executeExpression(context, Arrays.asList("value>150"), "")); + } + + @Test + public void testNumberCompString1() { + RuleRunner runner = QlExpressRunner.getInstance(); + Map context = new HashMap<>(); + context.put("value", ">160"); + Assert.assertTrue((Boolean) runner.executeExpression(context, Arrays.asList("value>150"), "")); + } + + @Test + public void testNumberCompString2() { + RuleRunner runner = QlExpressRunner.getInstance(); + Map context = new HashMap<>(); + context.put("value", "<160"); + Assert.assertTrue(runner.executeExpression(context, Arrays.asList("value>150"), "") == null); + } + + @Test + public void testNumberCompString3() { + RuleRunner runner = QlExpressRunner.getInstance(); + Map context = new HashMap<>(); + context.put("value", "<160ng/ml"); + Assert.assertTrue(runner.executeExpression(context, Arrays.asList("value>150"), "") == null); + } + + @Test + public void testNumberCompString4() { + RuleRunner runner = QlExpressRunner.getInstance(); + Map context = new HashMap<>(); + context.put("value", "<0.1234ng/ml"); + Assert.assertTrue((Boolean) runner.executeExpression(context, Arrays.asList("value<0.3"), "")); + } + + @Test + public void testNormal() { + RuleRunner runner = QlExpressRunner.getInstance(); + Map context = new HashMap<>(); + context.put("value", 160); + Assert.assertTrue( + (Boolean) runner.executeExpression(context, Arrays.asList("value > 150"), "")); + } + + @Test + public void testCombination() { + RuleRunner runner = QlExpressRunner.getInstance(); + Map context = new HashMap<>(); + context.put("value", "高"); + Assert.assertTrue( + (Boolean) + runner.executeExpression(context, Arrays.asList("value > 150 || value == '高'"), "")); + } +} diff --git a/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/logic/rule/RuleExecutorTests.java b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/logic/rule/RuleExecutorTests.java index 59c7d80c6..89423e77d 100644 --- a/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/logic/rule/RuleExecutorTests.java +++ b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/logic/rule/RuleExecutorTests.java @@ -46,17 +46,4 @@ public void testRuleExe() { Arrays.asList(new Entity("临床并发症", "高血压分层")), session, new RuleExecutor(), logger); Assert.assertTrue(ret); } - - @Test - public void testRuleExeInContext() { - Rule rule = getR1(); - Node root = rule.getRoot(); - TreeLogger logger = new TreeLogger(root.toString()); - Map session = new HashMap<>(); - session.put("伸缩压", 141); - session.put("症状", "有并发症的糖尿病"); - session.put(new Entity("临床并发症", "高血压分层").toString(), true); - Boolean ret = rule.getRoot().accept(Arrays.asList(), session, new RuleExecutor(), logger); - Assert.assertTrue(ret); - } } diff --git a/reasoner/thinker/src/test/resources/Hypertension.txt b/reasoner/thinker/src/test/resources/Hypertension.txt new file mode 100644 index 000000000..0dd1fb5a1 --- /dev/null +++ b/reasoner/thinker/src/test/resources/Hypertension.txt @@ -0,0 +1,63 @@ +Define (血压水平分级/`1级高血压`) { + R1: 收缩压>=140 or 舒张压>=90 +} + +Define (血压水平分级/`2级高血压`) { + R1: 收缩压>=160 or 舒张压>=100 +} + +Define (血压水平分级/`血压正常高值`) { + R1: 收缩压>=120 or 舒张压>=80 +} + +Define (疾病/`高血压`) { + R1: 血压水平分级/`1级高血压` or 血压水平分级/`2级高血压` +} + +Define (疾病/`肥胖`) { + R1: BMI>=30 +} + +Define (疾病/`慢性肾病3期`) { + R1: GFR>=30 +} + +Define (高血压分层/`心血管危险因素-肥胖`) { + R1: 疾病/`肥胖` + R2: 疾病/`腹型肥胖` +} + +Define (高血压分层/`靶器官损害-肾脏`) { + R1: 疾病/`慢性肾病3期` +} + +Define (高血压分层/`临床并发症-心脏疾病`) { + R1: 疾病/`冠心病` + R2: 疾病/`心力衰竭` + R3: 疾病/`心房颤动` +} + +Define (高血压分层/`心血管危险因素`) { + R1: 高血压分级/`心血管危险因素-肥胖` +} + +Define (高血压分层/`靶器官损害`) { + R1: 高血压分级/`靶器官损害-肾脏` +} + +Define (高血压分层/`临床并发症`) { + R1: 高血压分级/`临床并发症-心脏疾病` +} + +Define (药品/`多药方案`) { + R1: 收缩压>=160 and 舒张压>=100 + R2: 收缩压-目标收缩压上界>=20 +} + +Define ()-[:基本用药方案]->(:药品/`ACEI+噻嗪类利尿剂`) { + R1: 疾病/`高血压` and 药品/`多药方案` +} + +Define ()-[:基本用药方案]->(:药品/`ARB+噻嗪类利尿剂`) { + R1: 疾病/`高血压` and 药品/`多药方案` +} \ No newline at end of file diff --git a/reasoner/thinker/src/test/resources/InsuranceRules.txt b/reasoner/thinker/src/test/resources/InsuranceRules.txt new file mode 100644 index 000000000..b1f4f3623 --- /dev/null +++ b/reasoner/thinker/src/test/resources/InsuranceRules.txt @@ -0,0 +1,16 @@ +Define(a:InsDisease)-[:directEvolve]->(b:InsDisease) { + R1: (a)-[:evolve]->(b) +} + +Define(a:InsDisease)-[:child]->(c:InsDisease) { + R1: (a)-[:child]->(b: InsDisease) AND (b)-[:child]->(c) +} + +Define(a:InsDisease)-[:inDirectEvolve]->(c:InsDisease) { + R1: (a)-[:child]->(b: InsDisease) AND (b)-[:evolve]->(c) +} + +Define(a:InsDisease)-[:disclaim]->(d:InsComProd) { + R1: (a)-[p:disclaimClause]->(b: InsDiseaseDisclaim) AND (b)-[:clauseVersion]->(c:InsClause) AND (c)-[:insClauseVersion]->(d) AND (p.disclaimType == '既往') +} + diff --git a/reasoner/thinker/src/test/resources/Medical.txt b/reasoner/thinker/src/test/resources/Medical.txt new file mode 100644 index 000000000..28bc9606e --- /dev/null +++ b/reasoner/thinker/src/test/resources/Medical.txt @@ -0,0 +1,27 @@ +Define (a:Med.Examination/`尿酸`)-[:abnormalValue]->(c: Med.ExaminationResult/`偏低`) { + R1: contains(population, '男性') AND (value<150 || value in ["低", "阴性"]) +} +Description: "对于男性,尿酸的正常范围是[150-416]umol/L" + +Define (a:Med.Examination/`尿酸`)-[:abnormalValue]->(c: Med.ExaminationResult/`偏高`) { + R1: contains(population, '男性') AND (value>416 || value in ["高", "阳性"]) +} +Description: "对于男性,尿酸的正常范围是[150-416]umol/L" + +Define (a:Med.Examination/`尿酸`)-[:abnormalValue]->(c: Med.ExaminationResult/`偏低`) { + R1: contains(population, '女性') AND (value<89 || value in ["低", "阴性"]) +} +Description: "对于女性,尿酸的正常范围是[89-357]umol/L" + +Define (a:Med.Examination/`尿酸`)-[:abnormalValue]->(c: Med.ExaminationResult/`偏高`) { + R1: contains(population, '女性') AND (value>357 || value in ["高", "阳性"]) +} +Description: "对于女性,尿酸的正常范围是[89-357]umol/L" + +Define (a:Med.Examination)-[:abnormalRule]->(c: string) { + R1: (a)-[:abnormalValue]->(b: Med.ExaminationResult/`偏高`) AND (a)-[: highExplain]->(c) +} + +Define (a:Med.Examination)-[:abnormalRule]->(c: string) { + R1: (a)-[:abnormalValue]->(b: Med.ExaminationResult/`偏低`) AND (a)-[: lowExplain]->(c) +} \ No newline at end of file diff --git a/reasoner/thinker/src/test/scala/com/antgroup/openspg/reasoner/thinker/SimplifyThinkerParserTest.scala b/reasoner/thinker/src/test/scala/com/antgroup/openspg/reasoner/thinker/SimplifyThinkerParserTest.scala index 12be2c394..da4d4ea4a 100644 --- a/reasoner/thinker/src/test/scala/com/antgroup/openspg/reasoner/thinker/SimplifyThinkerParserTest.scala +++ b/reasoner/thinker/src/test/scala/com/antgroup/openspg/reasoner/thinker/SimplifyThinkerParserTest.scala @@ -16,7 +16,7 @@ package com.antgroup.openspg.reasoner.thinker import scala.collection.JavaConverters._ import scala.collection.mutable -import com.antgroup.openspg.reasoner.thinker.logic.graph.{Entity, Predicate} +import com.antgroup.openspg.reasoner.thinker.logic.graph.{Entity, Predicate, Value} import com.antgroup.openspg.reasoner.thinker.logic.graph import com.antgroup.openspg.reasoner.thinker.logic.rule._ import com.antgroup.openspg.reasoner.thinker.logic.rule.exact._ @@ -221,7 +221,7 @@ class SimplifyThinkerParserTest extends AnyFunSpec { new graph.Triple( new Predicate("disclaimClause", "p"), new Predicate("disclaimType"), - new graph.Any())), + new Value(null, "anonymous_3"))), new TriplePattern( new graph.Triple( new graph.Node("InsClause", "c"), @@ -240,7 +240,7 @@ class SimplifyThinkerParserTest extends AnyFunSpec { child .asInstanceOf[QlExpressCondition] .getQlExpress - .equals("hits(get_spo(a, p, b),get_spo(c, anonymous_8, d)) > 2")) + .equals("hits(get_spo(a, p, b),get_spo(c, anonymous_4, d)) > 2")) } }) } @@ -258,4 +258,28 @@ class SimplifyThinkerParserTest extends AnyFunSpec { (entityCount, tripleCount) } + it("define_rule_on_relation_to_concept3") { + val thinkerDsl = + """ + |Define(a:InsDisease)-[:abnormalRule]->(o:String) { + | R1: (a)-[p: disclaimClause]->(b: InsDiseaseDisclaim) AND (b)-[:interpretation]->(o) + |} + |""".stripMargin + val rule: Rule = parser.parseSimplifyDsl(thinkerDsl).head + val head = rule.getHead.asInstanceOf[TriplePattern].getTriple + assert(head.getObject.isInstanceOf[Value]) + val conditionList = rule.getRoot.asInstanceOf[And].getChildren + assert(conditionList.size == 2) + val expectedConditionList = List("get_spo(a,p,b)", "get_spo(b,anonymous_2,o)") + conditionList.containsAll(expectedConditionList.asJava) + assert(rule.getBody.size() == 2) + rule.getBody.asScala.foreach(clause => { + val triple = clause.asInstanceOf[TriplePattern].getTriple + if (triple.getSubject.alias().equals("b")) { + assert(triple.getPredicate.alias().equals("anonymous_2")) + assert(triple.getObject.alias().equals("o")) + } + }) + + } } 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 e67633cfd..ca70000cc 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 @@ -19,11 +19,7 @@ import com.antgroup.openspg.reasoner.udf.UdfMngFactory; import com.antgroup.openspg.reasoner.udf.model.RuntimeUdfMeta; import com.antgroup.openspg.reasoner.udf.model.UdfOperatorTypeEnum; -import com.antgroup.openspg.reasoner.udf.rule.op.OperatorEqualsLessMore; -import com.antgroup.openspg.reasoner.udf.rule.op.OperatorGetValue; -import com.antgroup.openspg.reasoner.udf.rule.op.OperatorIn; -import com.antgroup.openspg.reasoner.udf.rule.op.OperatorLike; -import com.antgroup.openspg.reasoner.udf.rule.op.OperatorMultiDiv; +import com.antgroup.openspg.reasoner.udf.rule.op.*; import com.antgroup.openspg.reasoner.udf.rule.udf.UdfWrapper; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; @@ -33,12 +29,7 @@ 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.*; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,7 +41,7 @@ public class RuleRunner { private static final Cache> contextCache = CacheBuilder.newBuilder().maximumSize(100).expireAfterWrite(24, TimeUnit.HOURS).build(); - private final ExpressRunner EXPRESS_RUNNER = new ExpressRunner(); + protected final ExpressRunner EXPRESS_RUNNER = new ExpressRunner(); private static final Set keywordSet = new HashSet<>(); @@ -155,7 +146,7 @@ public Object executeExpression( return null; } - private RuleRunner() {} + protected RuleRunner() {} private static volatile RuleRunner instance = null; @@ -173,7 +164,7 @@ public static RuleRunner getInstance() { return instance; } - private void init() { + protected void init() { // disable print error // InstructionSet.printInstructionError = false; // use short circuit @@ -181,6 +172,7 @@ private void init() { registerUdf(); overrideOperator(); EXPRESS_RUNNER.addFunction("get_value", new OperatorGetValue()); + EXPRESS_RUNNER.addFunction("get_spo", new OperatorGetSPO()); } /** register all udfs */ @@ -207,7 +199,7 @@ private void registerUdf() { } } - private void overrideOperator() { + protected void overrideOperator() { Lists.newArrayList( new Tuple2("<", new OperatorEqualsLessMore("<")), new Tuple2(">", new OperatorEqualsLessMore(">")), diff --git a/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/rule/op/OperatorEqualsLessMore.java b/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/rule/op/OperatorEqualsLessMore.java index 50f260c45..4fb9bab28 100644 --- a/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/rule/op/OperatorEqualsLessMore.java +++ b/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/rule/op/OperatorEqualsLessMore.java @@ -98,13 +98,13 @@ public static Integer compareDataWithoutException(Object op1, Object op2) { return compareResult; } - private static final int NUMBER_TYPE_BYTE = 1; - private static final int NUMBER_TYPE_SHORT = 2; - private static final int NUMBER_TYPE_INT = 3; - private static final int NUMBER_TYPE_LONG = 4; - private static final int NUMBER_TYPE_FLOAT = 5; - private static final int NUMBER_TYPE_DOUBLE = 6; - private static final int NUMBER_TYPE_DECIMAL = 7; + protected static final int NUMBER_TYPE_BYTE = 1; + protected static final int NUMBER_TYPE_SHORT = 2; + protected static final int NUMBER_TYPE_INT = 3; + protected static final int NUMBER_TYPE_LONG = 4; + protected static final int NUMBER_TYPE_FLOAT = 5; + protected static final int NUMBER_TYPE_DOUBLE = 6; + protected static final int NUMBER_TYPE_DECIMAL = 7; public static int getSeq(Class aClass) { if (aClass == Byte.class || aClass == byte.class) { diff --git a/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/rule/op/OperatorGetSPO.java b/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/rule/op/OperatorGetSPO.java new file mode 100644 index 000000000..2f8ca53df --- /dev/null +++ b/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/rule/op/OperatorGetSPO.java @@ -0,0 +1,36 @@ +/* + * 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.udf.rule.op; + +import com.ql.util.express.ArraySwap; +import com.ql.util.express.InstructionSetContext; +import com.ql.util.express.OperateData; +import com.ql.util.express.instruction.OperateDataCacheManager; +import com.ql.util.express.instruction.op.OperatorBase; +import com.ql.util.express.instruction.opdata.OperateDataAttr; +import org.apache.commons.lang3.StringUtils; + +public class OperatorGetSPO extends OperatorBase { + @Override + public OperateData executeInner(InstructionSetContext parent, ArraySwap list) throws Exception { + Object[] parameterNames = new Object[list.length]; + + for (int i = 0; i < list.length; ++i) { + parameterNames[i] = ((OperateDataAttr) list.get(i)).getName(); + } + String key = StringUtils.join(parameterNames, "_"); + Object result = parent.getParent().get(key); + return OperateDataCacheManager.fetchOperateData(result, Object.class); + } +} From 67a0f5c2365ebfc8c9ed3781d779e752d475c408 Mon Sep 17 00:00:00 2001 From: wangshaofei Date: Mon, 1 Jul 2024 11:28:18 +0800 Subject: [PATCH 22/29] feat(reasoner): upgrade thinker dsl (#314) --- .../com/antgroup/openspg/reasoner/KGDSL.g4 | 6 +- .../logic/graph/CombinationEntity.java | 92 +++++++++++++++++++ .../reasoner/thinker/ThinkerRuleParser.scala | 53 ++++++----- .../thinker/SimplifyThinkerParserTest.scala | 34 ++++++- 4 files changed, 160 insertions(+), 25 deletions(-) create mode 100644 reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/CombinationEntity.java diff --git a/reasoner/kgdsl-parser/src/main/antlr4/com/antgroup/openspg/reasoner/KGDSL.g4 b/reasoner/kgdsl-parser/src/main/antlr4/com/antgroup/openspg/reasoner/KGDSL.g4 index b3e9c09ad..9a860da98 100644 --- a/reasoner/kgdsl-parser/src/main/antlr4/com/antgroup/openspg/reasoner/KGDSL.g4 +++ b/reasoner/kgdsl-parser/src/main/antlr4/com/antgroup/openspg/reasoner/KGDSL.g4 @@ -208,7 +208,8 @@ element_variable_declaration : element_variable ; element_variable : identifier ; label_expression_lookup: vertical_bar label_name; label_expression : label_name label_expression_lookup* ; -label_name : entity_type | concept_name; +combination_concept : concept_name plus_sign concept_name (plus_sign concept_name)*; +label_name : entity_type | concept_name | combination_concept; entity_type : identifier | prefix_name; prefix_name : identifier period identifier ; concept_name : meta_concept_type solidus concept_instance_id ; @@ -1015,7 +1016,8 @@ description: the_description_symbol colon unbroken_character_string_literal; rule_and_action_body: left_brace rule_body_content (action_body_structure)? right_brace; -rule_body_content : (identifier explain? colon logical_statement)*; +rule_prefix: identifier explain? colon; +rule_body_content : rule_prefix? logical_statement (rule_prefix logical_statement)*; logical_statement : value_expression; diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/CombinationEntity.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/CombinationEntity.java new file mode 100644 index 000000000..08a7d560c --- /dev/null +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/CombinationEntity.java @@ -0,0 +1,92 @@ +/* + * 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.thinker.logic.graph; + +import com.alibaba.fastjson.JSON; +import java.util.List; +import java.util.Objects; + +public class CombinationEntity extends Element { + private List entityList; + private String alias; + + public CombinationEntity() {} + + public CombinationEntity(List entityList) { + this.entityList = entityList; + } + + public CombinationEntity(List entityList, String alias) { + this.entityList = entityList; + this.alias = alias; + } + + /** + * Getter method for property entityList. + * + * @return property value of entityList + */ + public List getEntityList() { + return entityList; + } + + /** + * Setter method for property entityList. + * + * @param entityList value to be assigned to property entityList + */ + public void setEntityList(List entityList) { + this.entityList = entityList; + } + + /** + * Getter method for property alias. + * + * @return property value of alias + */ + public String getAlias() { + return alias; + } + + /** + * Setter method for property alias. + * + * @param alias value to be assigned to property alias + */ + public void setAlias(String alias) { + this.alias = alias; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof CombinationEntity)) { + return false; + } + CombinationEntity that = (CombinationEntity) o; + return Objects.equals(entityList, that.entityList); + } + + @Override + public int hashCode() { + return Objects.hash(entityList, alias); + } + + @Override + public String toString() { + return JSON.toJSONString(entityList); + } +} diff --git a/reasoner/thinker/src/main/scala/com/antgroup/openspg/reasoner/thinker/ThinkerRuleParser.scala b/reasoner/thinker/src/main/scala/com/antgroup/openspg/reasoner/thinker/ThinkerRuleParser.scala index f3a28e0e5..f9caf0f72 100644 --- a/reasoner/thinker/src/main/scala/com/antgroup/openspg/reasoner/thinker/ThinkerRuleParser.scala +++ b/reasoner/thinker/src/main/scala/com/antgroup/openspg/reasoner/thinker/ThinkerRuleParser.scala @@ -13,39 +13,30 @@ package com.antgroup.openspg.reasoner.thinker -import java.util.Locale - -import scala.collection.JavaConverters._ -import scala.collection.mutable -import scala.collection.mutable.ListBuffer - import com.antgroup.openspg.reasoner.KGDSLParser._ -import com.antgroup.openspg.reasoner.lube.common.expr.{ - BEqual, - BinaryOpExpr, - BNotEqual, - ConceptExpr, - Expr, - TripleExpr -} +import com.antgroup.openspg.reasoner.lube.common.expr._ import com.antgroup.openspg.reasoner.lube.utils.transformer.impl.Expr2QlexpressTransformer import com.antgroup.openspg.reasoner.parser.expr.RuleExprParser -import com.antgroup.openspg.reasoner.thinker.logic.graph.{Element, Entity, Predicate, Value} import com.antgroup.openspg.reasoner.thinker.logic.graph +import com.antgroup.openspg.reasoner.thinker.logic.graph.{ + CombinationEntity, + Element, + Entity, + Predicate, + Value +} import com.antgroup.openspg.reasoner.thinker.logic.rule.{ ClauseEntry, EntityPattern, Node, TriplePattern } -import com.antgroup.openspg.reasoner.thinker.logic.rule.exact.{ - And, - Condition, - Not, - Or, - QlExpressCondition -} +import com.antgroup.openspg.reasoner.thinker.logic.rule.exact.{Not, _} +import java.util.Locale import org.apache.commons.lang3.StringUtils +import scala.collection.JavaConverters._ +import scala.collection.mutable +import scala.collection.mutable.ListBuffer class ThinkerRuleParser extends RuleExprParser { val expr2StringTransformer = new Expr2QlexpressTransformer() @@ -353,6 +344,24 @@ class ThinkerRuleParser extends RuleExprParser { } else if (valueTypeSet.contains(sType.toUpperCase())) { sNode = new Value(null, sAlias) } + val combinationConceptContext = elementPatternDeclaration + .element_lookup() + .label_expression() + .label_name() + .combination_concept() + if (combinationConceptContext != null) { + val conceptEntityList = new mutable.ListBuffer[Entity]() + combinationConceptContext + .concept_name() + .asScala + .foreach(concept => { + val conceptEntity = constructConceptEntity(concept) + conceptEntityList += conceptEntity + }) + val combinationEntity = new CombinationEntity(conceptEntityList.asJava) + combinationEntity.setAlias(sAlias) + sNode = combinationEntity + } } aliasToElementMap += (sAlias -> sNode) sNode diff --git a/reasoner/thinker/src/test/scala/com/antgroup/openspg/reasoner/thinker/SimplifyThinkerParserTest.scala b/reasoner/thinker/src/test/scala/com/antgroup/openspg/reasoner/thinker/SimplifyThinkerParserTest.scala index da4d4ea4a..c75d3de59 100644 --- a/reasoner/thinker/src/test/scala/com/antgroup/openspg/reasoner/thinker/SimplifyThinkerParserTest.scala +++ b/reasoner/thinker/src/test/scala/com/antgroup/openspg/reasoner/thinker/SimplifyThinkerParserTest.scala @@ -16,7 +16,13 @@ package com.antgroup.openspg.reasoner.thinker import scala.collection.JavaConverters._ import scala.collection.mutable -import com.antgroup.openspg.reasoner.thinker.logic.graph.{Entity, Predicate, Value} +import com.antgroup.openspg.reasoner.thinker.logic.graph.{ + CombinationEntity, + Entity, + Predicate, + Triple, + Value +} import com.antgroup.openspg.reasoner.thinker.logic.graph import com.antgroup.openspg.reasoner.thinker.logic.rule._ import com.antgroup.openspg.reasoner.thinker.logic.rule.exact._ @@ -258,6 +264,32 @@ class SimplifyThinkerParserTest extends AnyFunSpec { (entityCount, tripleCount) } + it("test combination concept") { + val thinkerDsl = + """ + |Define (:Med.drug)-[:联合用药方案]->(:药品/`噻嗪类利尿剂`+药品/`脂脉康胶囊`+药品/`藏青果颗粒`) { + | R1: 疾病/`高血压` and 药品/`多药方案` + |} + |""".stripMargin + val rule: Rule = parser.parseSimplifyDsl(thinkerDsl).head + assert(rule.getHead.isInstanceOf[TriplePattern]) + val triple: Triple = rule.getHead.asInstanceOf[TriplePattern].getTriple + assert(triple.getObject.isInstanceOf[CombinationEntity]) + val entityList = triple.getObject.asInstanceOf[CombinationEntity].getEntityList + assert(entityList.size() == 3) + } + + it("test remove first rule_prefix") { + val thinkerDsl = + """ + |Define(a:InsDisease)-[:disclaim]->(d:InsComProd) { + | 疾病/`高血压` and 疾病/`低血压` + |} + |""".stripMargin + val ruleList: List[Rule] = parser.parseSimplifyDsl(thinkerDsl) + assert(ruleList.size == 1) + } + it("define_rule_on_relation_to_concept3") { val thinkerDsl = """ From e8a7a1d4f074c4d935cdd96565c031d09702575d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=9F=B9=E9=BE=99?= Date: Mon, 1 Jul 2024 12:04:13 +0800 Subject: [PATCH 23/29] fix(reasoner): fix bugs for concept expand & add runtime trace data (#278) --- .../common/utils/LabelTypeUtils.scala | 31 ++ .../reasoner/lube/catalog/Catalog.scala | 3 +- .../lube/catalog/SemanticPropertyGraph.scala | 26 +- .../lube/common/pattern/Element.scala | 11 +- .../lube/common/pattern/Pattern.scala | 5 +- .../rules/ConvertToMetaConcept.scala | 13 +- .../logical/planning/SubQueryMerger.scala | 15 +- .../logical/planning/SubQueryPlanner.scala | 24 +- .../semantic/rules/MetaConceptExplain.scala | 11 +- .../reasoner/runner/local/rdg/LocalRDG.java | 88 +++- .../runner/local/LocalRunnerTest.java | 425 ++++++------------ .../loader/TestFanxiqianGraphLoader.java | 43 ++ .../KgReasonerAddEdgeWithPropertyTest.java | 132 ------ .../local/main/KgReasonerZijinLocalTest.java | 200 --------- .../main/group/concept/GroupConceptTest.java | 16 +- .../reasoner/pattern/PatternMatcher.java | 63 ++- .../reasoner/recorder/DefaultRecorder.java | 7 +- .../reasoner/recorder/EmptyRecorder.java | 7 +- .../reasoner/recorder/IExecutionRecorder.java | 7 +- .../recorder/action/DebugInfoWithRule.java | 45 ++ .../recorder/action/DebugInfoWithStartId.java | 31 +- .../recorder/action/SampleAction.java | 66 ++- .../reasoner/recorder/action/SubAction.java | 3 + .../openspg/reasoner/utils/RunnerUtil.java | 3 + .../openspg/reasoner/util/LoaderUtil.scala | 27 +- .../warehouse/utils/WareHouseUtils.java | 25 ++ 26 files changed, 579 insertions(+), 748 deletions(-) create mode 100644 reasoner/common/src/main/scala/com/antgroup/openspg/reasoner/common/utils/LabelTypeUtils.scala create mode 100644 reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/loader/TestFanxiqianGraphLoader.java delete mode 100644 reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/KgReasonerAddEdgeWithPropertyTest.java create mode 100644 reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/DebugInfoWithRule.java diff --git a/reasoner/common/src/main/scala/com/antgroup/openspg/reasoner/common/utils/LabelTypeUtils.scala b/reasoner/common/src/main/scala/com/antgroup/openspg/reasoner/common/utils/LabelTypeUtils.scala new file mode 100644 index 000000000..2d9d643e1 --- /dev/null +++ b/reasoner/common/src/main/scala/com/antgroup/openspg/reasoner/common/utils/LabelTypeUtils.scala @@ -0,0 +1,31 @@ +/* + * 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 + +object LabelTypeUtils { + + /** + * get label meta type + * @param sType + * @return + */ + def getMetaType(sType: String): String = { + if (sType.contains("/")) { + sType.split("/")(0) + } else { + sType + } + } + +} diff --git a/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/catalog/Catalog.scala b/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/catalog/Catalog.scala index 955a5d596..852c949dc 100644 --- a/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/catalog/Catalog.scala +++ b/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/catalog/Catalog.scala @@ -16,6 +16,7 @@ package com.antgroup.openspg.reasoner.lube.catalog import scala.collection.mutable import com.antgroup.openspg.reasoner.common.exception.{ConnectionNotFoundException, GraphAlreadyExistsException, GraphNotFoundException} +import com.antgroup.openspg.reasoner.common.utils.LabelTypeUtils import com.antgroup.openspg.reasoner.lube.catalog.struct.Field import com.antgroup.openspg.reasoner.lube.common.graph.IRGraph import com.antgroup.openspg.reasoner.udf.{UdfMng, UdfMngFactory} @@ -80,7 +81,7 @@ abstract class Catalog() extends Serializable { * @return */ def getConnection(typeName: String): Set[AbstractConnection] = { - val finalType = typeName.split("/")(0) + val finalType = LabelTypeUtils.getMetaType(typeName) if (!connections.contains(finalType)) { throw ConnectionNotFoundException(s"$finalType not found.", null) } diff --git a/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/catalog/SemanticPropertyGraph.scala b/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/catalog/SemanticPropertyGraph.scala index cdfb765ae..28f094149 100644 --- a/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/catalog/SemanticPropertyGraph.scala +++ b/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/catalog/SemanticPropertyGraph.scala @@ -18,6 +18,7 @@ import scala.collection.mutable import com.antgroup.openspg.reasoner.common.exception.NotDefineException import com.antgroup.openspg.reasoner.common.graph.edge.{Direction, SPO} import com.antgroup.openspg.reasoner.common.types.{KgType, KTString} +import com.antgroup.openspg.reasoner.common.utils.LabelTypeUtils import com.antgroup.openspg.reasoner.lube.catalog.struct.{Edge, Field, Node, NodeType} /** @@ -63,27 +64,32 @@ class SemanticPropertyGraph( } } + def getEdgeWithMetaType(spoStr: String): SPO = { + var spo = new SPO(spoStr) + if (!graphSchema.edges.contains(spo)) { + spo = new SPO(LabelTypeUtils.getMetaType(spo.getS), + spo.getP, LabelTypeUtils.getMetaType(spo.getO)) + } + spo + } + def getNode(nodeLabel: String): Node = { - graphSchema.nodes(nodeLabel) + val nodeLabelOnlyMetaType = LabelTypeUtils.getMetaType(nodeLabel) + graphSchema.nodes(nodeLabelOnlyMetaType) } def getEdge(spoStr: String): Edge = { - var spo = new SPO(spoStr) - if (spo.getP.equals("belongTo") && !graphSchema.edges.contains(spo)) { - spo = new SPO(spo.getS, spo.getP, spo.getO.split("/")(0)) - } + val spo = getEdgeWithMetaType(spoStr) graphSchema.edges(spo) } def containsNode(nodeLabel: String): Boolean = { - graphSchema.nodes.contains(nodeLabel) + val nodeLabelOnlyMetaType = LabelTypeUtils.getMetaType(nodeLabel) + graphSchema.nodes.contains(nodeLabelOnlyMetaType) } def containsEdge(spoStr: String): Boolean = { - var spo = new SPO(spoStr) - if (spo.getP.equals("belongTo") && !graphSchema.edges.contains(spo)) { - spo = new SPO(spo.getS, spo.getP, spo.getO.split("/")(0)) - } + val spo = getEdgeWithMetaType(spoStr) graphSchema.edges.contains(spo) } diff --git a/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/common/pattern/Element.scala b/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/common/pattern/Element.scala index e3deb061d..7eb7c2496 100644 --- a/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/common/pattern/Element.scala +++ b/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/common/pattern/Element.scala @@ -15,6 +15,8 @@ package com.antgroup.openspg.reasoner.lube.common.pattern import scala.language.implicitConversions +import com.antgroup.openspg.reasoner.common.types.KTString +import com.antgroup.openspg.reasoner.common.utils.LabelTypeUtils import com.antgroup.openspg.reasoner.lube.common.expr._ import com.antgroup.openspg.reasoner.lube.common.rule.{LogicRule, Rule} @@ -37,6 +39,11 @@ trait Element extends Serializable { */ case class PatternElement(alias: String, typeNames: Set[String], var rule: Rule) extends Element { + def getMetaTypeNames: Set[String] = { + typeNames.map(x => { + LabelTypeUtils.getMetaType(x) + }) + } override def toString: String = { val stringBuilder = StringBuilder.newBuilder stringBuilder.append("(").append(alias).append(":") @@ -71,7 +78,9 @@ object ElementOps { implicit def toPattenElement(element: Element): PatternElement = { element match { case EntityElement(id, label, alias) => - val rule = BinaryOpExpr(BEqual, UnaryOpExpr(GetField("id"), Ref(alias)), VString(id)) + val rule = FunctionExpr("contains_any", + List.apply(UnaryOpExpr(GetField("id"), + Ref(alias)), VList(List.apply(id), KTString))) PatternElement(alias, Set.apply(label), LogicRule(s"R_$alias", "", rule)) case patternElement: PatternElement => patternElement } diff --git a/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/common/pattern/Pattern.scala b/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/common/pattern/Pattern.scala index b6e180803..aa38a4277 100644 --- a/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/common/pattern/Pattern.scala +++ b/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/common/pattern/Pattern.scala @@ -16,6 +16,7 @@ package com.antgroup.openspg.reasoner.lube.common.pattern import scala.collection.mutable import com.antgroup.openspg.reasoner.common.graph.edge.{Direction, SPO} +import com.antgroup.openspg.reasoner.common.utils.LabelTypeUtils import com.antgroup.openspg.reasoner.lube.catalog.SemanticPropertyGraph sealed trait Pattern { @@ -162,9 +163,7 @@ case class GraphPattern( if (label.equals(compareLabel)) { return label } - if (nodeLabel.contains("/")) { - compareLabel = nodeLabel.split("/")(0) - } + compareLabel = LabelTypeUtils.getMetaType(nodeLabel) if (label.equals(compareLabel)) { return nodeLabel } diff --git a/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/optimizer/rules/ConvertToMetaConcept.scala b/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/optimizer/rules/ConvertToMetaConcept.scala index f5135e53d..174b769eb 100644 --- a/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/optimizer/rules/ConvertToMetaConcept.scala +++ b/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/optimizer/rules/ConvertToMetaConcept.scala @@ -13,15 +13,18 @@ package com.antgroup.openspg.reasoner.lube.logical.optimizer.rules +import scala.collection.mutable + import com.antgroup.openspg.reasoner.common.exception.InvalidGraphException import com.antgroup.openspg.reasoner.common.types.KTString -import com.antgroup.openspg.reasoner.lube.common.expr.{BinaryOpExpr, _} +import com.antgroup.openspg.reasoner.common.utils.LabelTypeUtils +import com.antgroup.openspg.reasoner.lube.common.expr._ import com.antgroup.openspg.reasoner.lube.common.pattern.{NodePattern, PartialGraphPattern, PatternElement} import com.antgroup.openspg.reasoner.lube.common.rule.LogicRule import com.antgroup.openspg.reasoner.lube.logical.operators.{ExpandInto, LogicalOperator, PatternScan} import com.antgroup.openspg.reasoner.lube.logical.optimizer.{Direction, SimpleRule, Up} import com.antgroup.openspg.reasoner.lube.logical.planning.LogicalPlannerContext -import scala.collection.mutable + object ConvertToMetaConcept extends SimpleRule { @@ -71,7 +74,7 @@ object ConvertToMetaConcept extends SimpleRule { if (types.isEmpty) { patternElement } else { - val metaConcepts = types.map(_.split("/").head) + val metaConcepts = types.map(LabelTypeUtils.getMetaType(_)) if (metaConcepts.size > 1) { throw InvalidGraphException("Entities must belong to the same meta concept") } @@ -80,7 +83,9 @@ object ConvertToMetaConcept extends SimpleRule { LogicRule( "metaConceptRule", "belongToConcept", - BinaryOpExpr(BIn, Ref(patternElement.alias + ".id"), VList(Ids.toList, KTString))) + FunctionExpr("contains_any", + List.apply(UnaryOpExpr(GetField("id"), + Ref(patternElement.alias)), VList(Ids.toList, KTString)))) patternElement.copy(typeNames = metaConcepts, rule = rule) } } diff --git a/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/planning/SubQueryMerger.scala b/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/planning/SubQueryMerger.scala index fa56b807f..0b2da8ee1 100644 --- a/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/planning/SubQueryMerger.scala +++ b/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/planning/SubQueryMerger.scala @@ -17,6 +17,7 @@ import scala.collection.mutable import com.antgroup.openspg.reasoner.common.graph.edge.{Direction, SPO} import com.antgroup.openspg.reasoner.common.trees.TopDown +import com.antgroup.openspg.reasoner.common.utils.LabelTypeUtils import com.antgroup.openspg.reasoner.lube.catalog.Catalog import com.antgroup.openspg.reasoner.lube.common.pattern.Pattern import com.antgroup.openspg.reasoner.lube.logical.{EdgeVar, NodeVar, RepeatPathVar, SolvedModel, Var} @@ -90,7 +91,7 @@ class SubQueryMerger(val dag: Dag[LogicalOperator])(implicit context: LogicalPla if (conn.direction == Direction.OUT) pattern.getNode(conn.target).typeNames else pattern.getNode(conn.source).typeNames conn.relTypes.contains(spo.getP) && (types.contains( - getMetaType(spo.getO)) || types.contains(spo.getO)) + LabelTypeUtils.getMetaType(spo.getO)) || types.contains(spo.getO)) }) .head .direction @@ -112,12 +113,12 @@ class SubQueryMerger(val dag: Dag[LogicalOperator])(implicit context: LogicalPla conn.relTypes.contains(spo.getP) && pattern .getNode(conn.target) .typeNames - .contains(getMetaType(spo.getO)) + .contains(LabelTypeUtils.getMetaType(spo.getO)) } else { conn.relTypes.contains(spo.getP) && pattern .getNode(conn.source) .typeNames - .contains(getMetaType(spo.getO)) + .contains(LabelTypeUtils.getMetaType(spo.getO)) } }) .head @@ -131,12 +132,4 @@ class SubQueryMerger(val dag: Dag[LogicalOperator])(implicit context: LogicalPla defined.toSet } - private def getMetaType(sType: String) = { - if (sType.contains("/")) { - sType.split("/")(0) - } else { - sType - } - } - } diff --git a/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/planning/SubQueryPlanner.scala b/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/planning/SubQueryPlanner.scala index 9bae3fa1c..914a46a7e 100644 --- a/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/planning/SubQueryPlanner.scala +++ b/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/planning/SubQueryPlanner.scala @@ -17,18 +17,12 @@ import scala.collection.mutable import com.antgroup.openspg.reasoner.common.constants.Constants import com.antgroup.openspg.reasoner.common.graph.edge.{Direction, SPO} -import com.antgroup.openspg.reasoner.common.utils.ParameterUtils +import com.antgroup.openspg.reasoner.common.utils.{LabelTypeUtils, ParameterUtils} import com.antgroup.openspg.reasoner.lube.Logging import com.antgroup.openspg.reasoner.lube.block._ import com.antgroup.openspg.reasoner.lube.catalog.{Catalog, SemanticRule, TemplateSemanticRule} import com.antgroup.openspg.reasoner.lube.common.pattern.Pattern -import com.antgroup.openspg.reasoner.lube.logical.{ - EdgeVar, - NodeVar, - RepeatPathVar, - SolvedModel, - Var -} +import com.antgroup.openspg.reasoner.lube.logical.{EdgeVar, NodeVar, RepeatPathVar, SolvedModel, Var} import com.antgroup.openspg.reasoner.lube.logical.operators._ import com.antgroup.openspg.reasoner.lube.logical.planning.SubQueryPlanner.nodeName import com.antgroup.openspg.reasoner.lube.logical.validate.Dag @@ -157,7 +151,7 @@ class SubQueryPlanner(val dag: Dag[Block])(implicit context: LogicalPlannerConte if (conn.direction == Direction.OUT) pattern.getNode(conn.target).typeNames else pattern.getNode(conn.source).typeNames conn.relTypes.contains(spo.getP) && (types.contains( - getMetaType(spo.getO)) || types.contains(spo.getO)) + LabelTypeUtils.getMetaType(spo.getO)) || types.contains(spo.getO)) }) .head .direction @@ -179,12 +173,12 @@ class SubQueryPlanner(val dag: Dag[Block])(implicit context: LogicalPlannerConte conn.relTypes.contains(spo.getP) && pattern .getNode(conn.target) .typeNames - .contains(getMetaType(spo.getO)) + .contains(LabelTypeUtils.getMetaType(spo.getO)) } else { conn.relTypes.contains(spo.getP) && pattern .getNode(conn.source) .typeNames - .contains(getMetaType(spo.getO)) + .contains(LabelTypeUtils.getMetaType(spo.getO)) } }) .head @@ -198,14 +192,6 @@ class SubQueryPlanner(val dag: Dag[Block])(implicit context: LogicalPlannerConte defined.toSet } - private def getMetaType(sType: String) = { - if (sType.contains("/")) { - sType.split("/")(0) - } else { - sType - } - } - private def rewriteBlock(block: Block, startType: String, direction: Direction): Block = { val rewriteBlock = rewriteBlockDirection(block, direction) val alias = getRootAlias(rewriteBlock, startType, direction) diff --git a/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/validate/semantic/rules/MetaConceptExplain.scala b/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/validate/semantic/rules/MetaConceptExplain.scala index ae95e68b6..100775573 100644 --- a/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/validate/semantic/rules/MetaConceptExplain.scala +++ b/reasoner/lube-logical/src/main/scala/com/antgroup/openspg/reasoner/lube/logical/validate/semantic/rules/MetaConceptExplain.scala @@ -14,13 +14,8 @@ package com.antgroup.openspg.reasoner.lube.logical.validate.semantic.rules import com.antgroup.openspg.reasoner.lube.block.{Block, MatchBlock} -import com.antgroup.openspg.reasoner.lube.catalog.SemanticPropertyGraph -import com.antgroup.openspg.reasoner.lube.common.pattern.{ - Connection, - EntityElement, - GraphPattern, - PatternElement -} +import com.antgroup.openspg.reasoner.lube.catalog.{Catalog, SemanticPropertyGraph} +import com.antgroup.openspg.reasoner.lube.common.pattern.{Connection, EntityElement, GraphPattern, PatternElement} import com.antgroup.openspg.reasoner.lube.logical.planning.LogicalPlannerContext import com.antgroup.openspg.reasoner.lube.logical.validate.semantic.Explain import scala.collection.mutable @@ -40,7 +35,7 @@ object MetaConceptExplain extends Explain { if (metaConceptEdges.isEmpty) { p } else { - val kg = context.catalog.getKnowledgeGraph(); + val kg = context.catalog.getGraph(Catalog.defaultGraphName) val metaConceptMap: mutable.HashMap[String, Set[String]] = mutable.HashMap.empty metaConceptEdges.foreach(e => parseMetaConcept(kg, e, pattern, metaConceptMap)) val newNodes = pattern.nodes.map(n => diff --git a/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/rdg/LocalRDG.java b/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/rdg/LocalRDG.java index 0ac76b1d5..068bdcfac 100644 --- a/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/rdg/LocalRDG.java +++ b/reasoner/runner/local-runner/src/main/java/com/antgroup/openspg/reasoner/runner/local/rdg/LocalRDG.java @@ -27,6 +27,7 @@ import com.antgroup.openspg.reasoner.common.graph.vertex.IVertex; import com.antgroup.openspg.reasoner.common.graph.vertex.IVertexId; import com.antgroup.openspg.reasoner.common.graph.vertex.impl.Vertex; +import com.antgroup.openspg.reasoner.common.utils.LabelTypeUtils; import com.antgroup.openspg.reasoner.graphstate.GraphState; import com.antgroup.openspg.reasoner.graphstate.model.MergeTypeEnum; import com.antgroup.openspg.reasoner.kggraph.KgGraph; @@ -180,11 +181,61 @@ private java.util.Set getStartId(java.util.List> k return startIdSet; } - private java.util.Map> generateRuntimeLog( + private java.util.Map getStartIdWithHitRuleValue( + java.util.List> kgGraphList, java.util.List rules) { + java.util.Map startIdSet = new java.util.HashMap(); + java.util.List>> fields = new ArrayList<>(); + for (Rule rule : rules) { + fields.addAll(WareHouseUtils.getRuleUsedAliasEle(rule)); + } + for (KgGraph kgGraph : kgGraphList) { + java.util.Map context = + RunnerUtil.kgGraph2Context(RunnerUtil.getKgGraphInitContext(this.kgGraphSchema), kgGraph); + StringBuilder sb = new StringBuilder(""); + for (Tuple2> field : fields) { + Object value = context.get(field._1); + if (!(value instanceof java.util.Map)) { + continue; + } + java.util.Map values = + (java.util.Map) context.get(field._1); + for (String prop : field._2) { + if (!values.containsKey(prop)) { + continue; + } + sb.append(field._1).append(".").append(prop).append("=").append(values.get(prop)); + } + } + System.out.println(sb.toString()); + java.util.List> startVertexList = + kgGraph.getVertex(this.curRdgStartVertexAlias); + for (IVertex startId : startVertexList) { + startIdSet.put(startId.getId(), sb.toString()); + } + startVertexList = kgGraph.getVertex(this.startVertexAlias); + for (IVertex startId : startVertexList) { + startIdSet.put(startId.getId(), sb.toString()); + } + } + return startIdSet; + } + + private java.util.Map generateRuntimeLog( java.util.Set failedStartId, java.util.Set successStartId) { - java.util.Map> runtimeLogMap = new HashMap<>(); + java.util.Map runtimeLogMap = new HashMap<>(); + runtimeLogMap.put(SampleAction.FAILED_START_ID_KEY, Lists.newArrayList(failedStartId)); + runtimeLogMap.put(SampleAction.PASS_START_ID_KEY, Lists.newArrayList(successStartId)); + return runtimeLogMap; + } + + private java.util.Map generateRuntimeLog( + java.util.Set failedStartId, + java.util.Set successStartId, + java.util.Map ruleRuntimeValue) { + java.util.Map runtimeLogMap = new HashMap<>(); runtimeLogMap.put(SampleAction.FAILED_START_ID_KEY, Lists.newArrayList(failedStartId)); runtimeLogMap.put(SampleAction.PASS_START_ID_KEY, Lists.newArrayList(successStartId)); + runtimeLogMap.put(SampleAction.RULE_RUNTIME_VALUE, ruleRuntimeValue); return runtimeLogMap; } @@ -218,6 +269,7 @@ public LocalRDG( this.startVertexAlias = startVertexAlias; this.taskId = taskId; this.patternMatcher = new PatternMatcher(this.taskId, graphState); + this.patternMatcher.setDebugEnable(true); this.isCarryTraversalGraph = carryTraversalGraph; if (null == executionRecorder) { @@ -314,6 +366,8 @@ public KgGraph call() throws Exception { this.kgGraphList = newKgGraphList; this.kgGraphSchema = KgGraphSchema.convert2KgGraphSchema(pattern); + java.util.Map debugInfoWithRule = + getStartIdWithHitRuleValue(this.kgGraphList, patternRuleLists); log.info( "LocalRDG patternScan,root=" @@ -327,7 +381,7 @@ public KgGraph call() throws Exception { "patternScan(" + RunnerUtil.getReadablePattern(pattern) + ")", this.kgGraphList.size(), "SubPattern", - generateRuntimeLog(failedStartIdSet, afterStartId), + generateRuntimeLog(failedStartIdSet, afterStartId, debugInfoWithRule), patternRuleLists); return this; } @@ -594,6 +648,9 @@ public LocalRDG expandInto(PatternElement target, Pattern pattern) { java.util.Set failedStartIdSet = getRemoveHashSet(originStartId, afterStartId); this.kgGraphSchema = afterKgGraphSchema; this.kgGraphList = newKgGraphList; + java.util.Map debugInfoWithRule = + getStartIdWithHitRuleValue(this.kgGraphList, patternRuleLists); + log.info( "LocalRDG ExpandInto,patternRoot=" + pattern.root() @@ -604,7 +661,7 @@ public LocalRDG expandInto(PatternElement target, Pattern pattern) { this.executionRecorder.stageResultWithDetail( "expandInto(" + RunnerUtil.getReadablePattern(pattern) + ")", this.kgGraphList.size(), - generateRuntimeLog(failedStartIdSet, afterStartId), + generateRuntimeLog(failedStartIdSet, afterStartId, debugInfoWithRule), patternRuleLists); return this; } @@ -676,6 +733,8 @@ public LocalRDG filter(Rule rule) { count += resultList.size(); newKgGraphList.addAll(resultList); } + java.util.Map debugInfoWithRule = + getStartIdWithHitRuleValue(this.kgGraphList, Lists.newArrayList(rule)); java.util.Set originStartId = getStartId(this.kgGraphList); java.util.Set afterStartId = getStartId(newKgGraphList); java.util.Set failedStartIdSet = getRemoveHashSet(originStartId, afterStartId); @@ -684,7 +743,7 @@ public LocalRDG filter(Rule rule) { this.executionRecorder.stageResultWithDetail( "filter(" + rule.getName() + "," + exprStringSet + ")", this.kgGraphList.size(), - generateRuntimeLog(failedStartIdSet, afterStartId), + generateRuntimeLog(failedStartIdSet, afterStartId, debugInfoWithRule), Lists.newArrayList(rule)); return this; } @@ -1079,7 +1138,7 @@ private java.util.Map getProcessInfo( DebugInfoWithStartId startId, String bizId, String targetLabel, String targetId) { java.util.Map processInfo = startId.toJsonObj(); if (StringUtils.isNotBlank(targetLabel)) { - processInfo.put("targetLabel", targetLabel.split("/")[0]); + processInfo.put("targetLabel", LabelTypeUtils.getMetaType(targetLabel)); processInfo.put("targetId", targetId); } java.util.Map startIdInfo = new HashMap<>(); @@ -1148,9 +1207,22 @@ private void addVertex(AddVertex addVertex) { long count = 0; for (KgGraph kgGraph : this.kgGraphList) { IVertex willAddedVertex = impl.extractVertex(kgGraph); - this.graphState.addVertex(willAddedVertex); + IVertex storedVertex = + this.graphState.getVertex(willAddedVertex.getId(), null); + if (storedVertex != null) { + java.util.Map property = new HashMap<>(); + for (String key : willAddedVertex.getValue().getKeySet()) { + property.put(key, willAddedVertex.getValue().get(key)); + } + this.graphState.mergeVertexProperty( + storedVertex.getId(), property, MergeTypeEnum.REPLACE, 0L); + } else { + this.graphState.addVertex(willAddedVertex); + } + storedVertex = this.graphState.getVertex(willAddedVertex.getId(), null); + // add to result list - this.resultVertexSet.add(willAddedVertex); + this.resultVertexSet.add(storedVertex); count++; } diff --git a/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/LocalRunnerTest.java b/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/LocalRunnerTest.java index 08de4375e..faedc5e40 100644 --- a/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/LocalRunnerTest.java +++ b/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/LocalRunnerTest.java @@ -15,8 +15,10 @@ import com.alibaba.fastjson.JSON; import com.antgroup.openspg.reasoner.common.constants.Constants; +import com.antgroup.openspg.reasoner.common.graph.edge.Direction; import com.antgroup.openspg.reasoner.common.utils.PropertyUtil; import com.antgroup.openspg.reasoner.lube.catalog.Catalog; +import com.antgroup.openspg.reasoner.lube.catalog.GeneralSemanticRule; import com.antgroup.openspg.reasoner.lube.catalog.impl.PropertyGraphCatalog; import com.antgroup.openspg.reasoner.progress.ProgressReport; import com.antgroup.openspg.reasoner.recorder.DefaultRecorder; @@ -37,6 +39,142 @@ import scala.Tuple2; public class LocalRunnerTest { + @Test + public void testCreateConceptInstance() { + String rule = + "Define (s:Test.User)-[p:belongTo]->(o:`Test.UserFeature`/`白领`) {\n" + + " GraphStructure {\n" + + " (s)\n" + + " }\n" + + " Rule {\n" + + " r1(\"属于教师\") = s.nightTrader == 1\n" + + " r2(\"属于程序员\") = s.nightTrader == 2\n" + + " r3(\"属于医生\") = s.nightTrader == 3\n" + + " s_r1 = rule_value(r1, \"教师\", \"\")\n" + + " s_r2 = rule_value(r2, \"程序员\", s_r1)\n" + + " s_r3 = rule_value(r3, \"医生\", s_r2)\n" + + " R: s_r3 != \"\"\n" + + " }\n" + + " Action {\n" + + " sub_concept = createNodeInstance(\n" + + " type=Test.UserFeature,\n" + + " value={\n" + + " id=concat(\"白领-\", s_r3)\n" + + " }\n" + + " )\n" + + " createEdgeInstance(\n" + + " src=s,\n" + + " dst=sub_concept,\n" + + " type=belongTo,\n" + + " value={\n" + + " __to_id_type__='Test.UserFeature'\n" + + " __from_id_type__='Test.User'\n" + + " }\n" + + " )\n" + + " }\n" + + "}"; + String rule1 = + "Define (s:Test.User)-[p:belongTo]->(o:`Test.UserFeature`/`学生`) {\n" + + " GraphStructure {\n" + + " (s)\n" + + " }\n" + + " Rule {\n" + + " r1(\"就读幼儿园\") = s.nightTrader == 1\n" + + " r2(\"就读小学\") = s.nightTrader == 2\n" + + " r3(\"就读中学\") = s.nightTrader == 3\n" + + " s_r1 = rule_value(r1, \"就读幼儿园\", \"\")\n" + + " s_r2 = rule_value(r2, \"就读小学\", s_r1)\n" + + " s_r3 = rule_value(r3, \"就读中学\", s_r2)\n" + + " R: s_r3 != \"\"\n" + + " }\n" + + " Action {\n" + + " sub_concept = createNodeInstance(\n" + + " type=Test.UserFeature,\n" + + " value={\n" + + " id=concat(\"学生-\", s_r3)\n" + + " }\n" + + " )\n" + + " createEdgeInstance(\n" + + " src=s,\n" + + " dst=sub_concept,\n" + + " type=belongTo,\n" + + " value={\n" + + " __to_id_type__='Test.UserFeature'\n" + + " __from_id_type__='Test.User'\n" + + " }\n" + + " )\n" + + " }\n" + + "}"; + String dsl = + "match (s:Test.User)-[p:belongTo]->(o:Test.UserFeature)-[p2:newedge]->(o2:Test.TaxOfUserFeature) return s.id,p,o.id, o2.id" + + ""; + // String dsl = rule; + LocalReasonerTask task = new LocalReasonerTask(); + task.setDsl(dsl); + task.setGraphLoadClass( + "com.antgroup.openspg.reasoner.runner.local.loader.TestFanxiqianGraphLoader"); + task.getParams().put(Constants.SPG_REASONER_PLAN_PRETTY_PRINT_LOGGER_ENABLE, true); + task.getParams().put(Constants.SPG_REASONER_LUBE_SUBQUERY_ENABLE, true); + task.getParams().put(ConfigKey.KG_REASONER_OUTPUT_GRAPH, true); + task.setStartIdList(Lists.newArrayList(new Tuple2<>("张三", "Test.User"))); + task.setExecutionRecorder(new DefaultRecorder()); + task.setExecutorTimeoutMs(99999999999999999L); + + // add mock catalog + Map> schema = new HashMap<>(); + schema.put( + "Test.User", + Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("id", "name", "nightTrader"))); + schema.put( + "Test.UserFeature", Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("id", "name"))); + schema.put( + "Test.TaxOfUserFeature", + Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("id", "name"))); + + schema.put( + "Test.User_belongTo_Test.UserFeature", + Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet())); + + schema.put( + "Test.UserFeature_newedge_Test.TaxOfUserFeature", + Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet())); + + Catalog catalog = new PropertyGraphCatalog(Convert2ScalaUtil.toScalaImmutableMap(schema)); + catalog.init(); + catalog + .getGraph("KG") + .registerRule("Test.User_belongTo_Test.UserFeature/白领", new GeneralSemanticRule(rule)); + catalog + .getGraph("KG") + .addEdge( + "Test.User", + "belongTo", + "Test.UserFeature/白领", + Direction.OUT, + Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet()), + false); + catalog + .getGraph("KG") + .registerRule("Test.User_belongTo_Test.UserFeature/学生", new GeneralSemanticRule(rule1)); + catalog + .getGraph("KG") + .addEdge( + "Test.User", + "belongTo", + "Test.UserFeature/学生", + Direction.OUT, + Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet()), + false); + task.setCatalog(catalog); + + LocalReasonerRunner runner = new LocalReasonerRunner(); + LocalReasonerResult result = runner.run(task); + System.out.println(result); + System.out.println(task.getExecutionRecorder().toReadableString()); + Assert.assertEquals(result.getRows().size(), 2); + Assert.assertEquals(result.getRows().get(0)[0], "张三"); + clear(); + } @Test public void doTestFilter() { @@ -470,111 +608,6 @@ public void doTestLocalRunnerDependency() { System.out.println(task.getExecutionRecorder().toReadableString()); } - @Test - public void testComplexMultiDefineDsl() { - String dsl = - "// 当月交易量\n" - + "Define (s:CustFundKG.Account)-[p:cur_month_num]->(o:Int) {\n" - + " GraphStructure {\n" - + " (u:CustFundKG.Account)-[t:accountFundContact]-(s)\n" - + " }\n" - + "\tRule {\n" - + " \tR1(\"当月交易量\"): date_diff(from_unix_time(now(), 'yyyyMMdd'),t.transDate) <= 20\n" - + " o = group(s).count(u.id)\n" - + " }\n" - + "}\n" - + "// 次月交易量\n" - + "Define (s:CustFundKG.Account)-[p:last_month_num]->(o:Int) {\n" - + " GraphStructure {\n" - + " (u:CustFundKG.Account)-[t:accountFundContact]-(s)\n" - + " }\n" - + "\tRule {\n" - + " \tdate_delta = date_diff(from_unix_time(now(), 'yyyyMMdd'),t.transDate)\n" - + " \tR1(\"次月交易量\"): date_delta > 20 && date_delta <=40\n" - + " o = group(s).count(u.id)\n" - + " }\n" - + "}\n" - + "\n" - + "// 次次月交易量\n" - + "Define (s:CustFundKG.Account)-[p:last_last_month_num]->(o:Int) {\n" - + " GraphStructure {\n" - + " (u:CustFundKG.Account)-[t:accountFundContact]-(s)\n" - + " }\n" - + "\tRule {\n" - + " \tdate_delta = date_diff(from_unix_time(now(), 'yyyyMMdd'),t.transDate)\n" - + " \tR1(\"次月交易量\"): date_delta > 40 && date_delta <=60\n" - + " o = group(s).count(u.id)\n" - + " }\n" - + "}\n" - + "// 倍数\n" - + "Define (s:CustFundKG.Account)-[p:last_trans_multiple]->(o:Float) {\n" - + "\t\tGraphStructure {\n" - + " (s)\n" - + " }\n" - + "\tRule {\n" - + " \tmultiple = s.last_month_num*1.0 / s.last_last_month_num\n" - + " o = multiple\n" - + " }\n" - + "}\n" - + "\n" - + "// 倍数\n" - + "Define (s:CustFundKG.Account)-[p:cur_trans_multiple]->(o:Float) {\n" - + "\t\tGraphStructure {\n" - + " (s)\n" - + " }\n" - + "\tRule {\n" - + " \tmultiple = s.cur_month_num*1.0 / s.last_month_num\n" - + " o = multiple\n" - + " }\n" - + "}\n" - + "\n" - + "Define (s:CustFundKG.Account)-[p:is_trans_raise_more_after_down]->(o:Boolean) {\n" - + "\t\tGraphStructure {\n" - + " (s)\n" - + " }\n" - + "\tRule {\n" - + " \tR1(\"T月交易量级超过T-1月交易量级3倍\"): s.last_trans_multiple >=3\n" - + " \tR2(\"T+1月交易量级小于T月交易量级的1/2\"): s.cur_trans_multiple <0.5\n" - + " \to = rule_value(R1 && R2, true, false)\n" - + " }\n" - + "}\n" - + "\n" - + "GraphStructure {\n" - + " (s:CustFundKG.Account)\n" - + "}\n" - + "Rule {\n" - + "}\n" - + "Action {\n" - + "\tget(s.id, s.is_trans_raise_more_after_down, s.cur_trans_multiple, s.last_trans_multiple, s" - + ".last_last_month_num, s.last_month_num, s.cur_month_num)\n" - + "}\n"; - LocalReasonerTask task = new LocalReasonerTask(); - task.setDsl(dsl); - task.setGraphLoadClass( - "com.antgroup.openspg.reasoner.runner.local.loader.TestMultiVersionGraphLoader"); - - // add mock catalog - Map> schema = new HashMap<>(); - schema.put("CustFundKG.Account", Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("id"))); - schema.put( - "CustFundKG.Account_accountFundContact_CustFundKG.Account", - Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("sumAmt", "transDate"))); - - Catalog catalog = new PropertyGraphCatalog(Convert2ScalaUtil.toScalaImmutableMap(schema)); - catalog.init(); - task.getParams().put(ConfigKey.KG_REASONER_CATALOG, SimpleObjSerde.ser(catalog)); - - task.setStartIdList(Lists.newArrayList(new Tuple2<>("1", "CustFundKG.Account"))); - - LocalReasonerRunner runner = new LocalReasonerRunner(); - LocalReasonerResult result = runner.run(task); - System.out.println("##########################"); - System.out.println(result); - System.out.println("##########################"); - - clear(); - } - @Test public void testEdgePropertyDefineDsl2() { String dsl = @@ -646,27 +679,29 @@ public void testEdgePropertyDefineDsl2() { @Test public void testEdgePropertyDefineDsl() { String dsl = - "//1 先定义每两个用户间进行聚合交易\n" + "//1 先定义每两个用户间进行聚合交易总值\n" + "Define (s:CustFundKG.Account)-[p:tranTargetUser]->(o:CustFundKG.Account) {\n" + " GraphStructure {\n" - + " (o)<-[t:accountFundContact]-(s)\n" + + " (o)-[t:accountFundContact]-(s)\n" + " }\n" + "\tRule {\n" + " tran_count = group(s,o).count(t.sumAmt)\n" + + " tran_amt = group(s,o).sum(t.sumAmt)\n" + " p.tran_count = tran_count\n" + + " p.tran_amt = tran_amt\n" + " }\n" + "}\n" + "\n" + "\n" + "\n" - + "Define (s:CustFundKG.Account)-[p:is_repeat_tran_user]->(o:Int) {\n" + + "//在定义经常交易的用户流水总和\n" + + "Define (s:CustFundKG.Account)-[p:total_trans_amt]->(o:Int) {\n" + " GraphStructure {\n" + " (s)-[t:tranTargetUser]->(u:CustFundKG.Account)\n" + " }\n" + "\tRule {\n" - + " user_num(\"交易笔数大于3的重复用户个数\") = group(s).countIf(t.tran_count>=3, u.id)\n" - + " R1(\"超过10个\"): user_num > 10\n" - + " o = rule_value(R1, true, false)\n" + + " R(\"交易笔数大于1才保留\") : t.tran_count>=1\n" + + " o = group(s).sum(t.tran_amt)\n" + " }\n" + "}\n" + "\n" @@ -677,7 +712,7 @@ public void testEdgePropertyDefineDsl() { + "Rule {\n" + "}\n" + "Action{\n" - + "\tget(s.id, s.is_repeat_tran_user)\n" + + "\tget(s.id, s.total_trans_amt)\n" + "}"; LocalReasonerTask task = new LocalReasonerTask(); task.setDsl(dsl); @@ -706,178 +741,16 @@ public void testEdgePropertyDefineDsl() { clear(); } - @Test - public void testComplexMultiTimeUDFDefineDsl() { - String dsl = - "//1 先定义流入资金总数\n" - + "Define (s:CustFundKG.Account)-[p:total_in_trans_num]->(o:Int) {\n" - + " GraphStructure {\n" - + " (u:CustFundKG.Account)-[t:accountFundContact]->(s)\n" - + " }\n" - + "\tRule {\n" - + " o = group(s).count(t.sumAmt)\n" - + " }\n" - + "}\n" - + "\n" - + "//2 定义整百流入笔数\n" - + "Define (s:CustFundKG.Account)-[p:multiples_hundred_in_trans_num]->(o:Int) {\n" - + " GraphStructure {\n" - + " (u:CustFundKG.Account)-[t:accountFundContact]->(s)\n" - + " }\n" - + "\tRule {\n" - + " \tR1(\"必须是整百交易\"): t.sumAmt % 100 == 0\n" - + " o = group(s).count(t.sumAmt)\n" - + " }\n" - + "}\n" - + "\n" - + "// 判断是否汇聚赌博\n" - + "Define (s:CustFundKG.Account)-[p:is_pooling_gambling_funds]->(o:Float) {\n" - + " GraphStructure {\n" - + " (s)\n" - + " }\n" - + "\tRule {\n" - + " R0(\"存在流入和整百资金\"): s.multiples_hundred_in_trans_num != null && s.total_in_trans_num != null\n" - + " \tR1(\"流入整百金额笔数大于2比\"): s.multiples_hundred_in_trans_num > 2\n" - + " R2(\"整百交易占比大于2%\"): s.multiples_hundred_in_trans_num /cast_type(s.total_in_trans_num,'double') > 0.02\n" - + " o = rule_value(R2, true, false)\n" - + " }\n" - + "}\n" - + "\n" - + "//获取结果\n" - + "GraphStructure {\n" - + "\ts [CustFundKG.Account]\n" - + "}\n" - + "Rule {\n" - + "}\n" - + "Action{\n" - + "\tget(s.id, s.is_pooling_gambling_funds, s.multiples_hundred_in_trans_num, s.total_in_trans_num)\n" - + "}"; - - LocalReasonerTask task = new LocalReasonerTask(); - task.setDsl(dsl); - task.setGraphLoadClass( - "com.antgroup.openspg.reasoner.runner.local.loader.TestMultiVersionGraphLoader"); - - // add mock catalog - Map> schema = new HashMap<>(); - schema.put("CustFundKG.Account", Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("id"))); - schema.put( - "CustFundKG.Account_accountFundContact_CustFundKG.Account", - Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("sumAmt"))); - - Catalog catalog = new PropertyGraphCatalog(Convert2ScalaUtil.toScalaImmutableMap(schema)); - catalog.init(); - task.getParams().put(ConfigKey.KG_REASONER_CATALOG, SimpleObjSerde.ser(catalog)); - - task.setStartIdList(Lists.newArrayList(new Tuple2<>("1", "CustFundKG.Account"))); - - LocalReasonerRunner runner = new LocalReasonerRunner(); - LocalReasonerResult result = runner.run(task); - System.out.println("##########################"); - System.out.println(result); - System.out.println("##########################"); - - clear(); - } - - @Test - public void doTestComplexDefineDsl() { - String dsl = - "// 1\n" - + "Define (s:DomainFamily)-[p:blackRelateRate]->(o:Pkg) {\n" - + " GraphStructure {\n" - + " (o)-[:use]->(d:Domain)-[:belong]->(s)\n" - + " }\n" - + " Rule {\n" - + " R1: o.is_black == true\n" - // + " domain_num = group(s,o).count(d)\n" - // + " p.same_domain_num = domain_num\n" - + " }\n" - + "}\n" - + "\n" - + "// 2\n" - + "Define (s:DomainFamily)-[p:total_domain_num]->(o:Int) {\n" - + " GraphStructure {\n" - + " (s)<-[:belong]-(d:Domain)\n" - + " }\n" - + " Rule {\n" - + " o = group(s).count(d.id)\n" - + " }\n" - + "}\n" - + "\n" - + "// 3\n" - + "Define (s:Pkg)-[p:target]->(o:User) {\n" - + " GraphStructure {\n" - + " (s)<-[p1:blackRelateRate]-(df:DomainFamily),\n" - + " (df)<-[:belong]-(d:Domain),\n" - + " (o)-[:visit]->(d)\n" - + " } Rule {\n" - + " visit_time = group(o, df).count(d.id)\n" - + " R1(\"必须大于1次\"): visit_time >= 1\n" - + " R2(\"必须占比大于50%\"): 1.0 * visit_time / df.total_domain_num > 0.2\n" - + " }\n" - + "}\n" - + "\n" - + "// 4\n" - + "GraphStructure {\n" - + " (s:Pkg)-[p:target]->(o:User)\n" - + "}\n" - + "Rule {\n" - + "\n" - + "}\n" - + "Action {\n" - + " get(s.id,o.id)\n" - + "}"; - - LocalReasonerTask task = new LocalReasonerTask(); - task.setDsl(dsl); - task.setGraphLoadClass( - "com.antgroup.openspg.reasoner.runner.local.loader.TestDefineGraphLoader"); - - // add mock catalog - Map> schema = new HashMap<>(); - schema.put( - "DomainFamily", - Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("id", "total_domain_num"))); - schema.put("Pkg", Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("id", "is_black"))); - schema.put("Domain", Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("id"))); - schema.put("User", Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("id"))); - schema.put("Pkg_use_Domain", Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet())); - schema.put( - "Domain_belong_DomainFamily", Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet())); - schema.put("User_visit_Domain", Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet())); - - // define - schema.put( - "DomainFamily_blackRelateRate_Pkg", - Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet())); - schema.put("Pkg_target_User", Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet())); - Catalog catalog = new PropertyGraphCatalog(Convert2ScalaUtil.toScalaImmutableMap(schema)); - catalog.init(); - task.getParams().put(ConfigKey.KG_REASONER_CATALOG, SimpleObjSerde.ser(catalog)); - - task.setStartIdList(Lists.newArrayList(new Tuple2<>("black_app_1", "Pkg"))); - task.setExecutionRecorder(new DefaultRecorder()); - - LocalReasonerRunner runner = new LocalReasonerRunner(); - LocalReasonerResult result = runner.run(task); - System.out.println("##########################"); - System.out.println(result); - System.out.println("##########################"); - System.out.println(task.getExecutionRecorder().toReadableString()); - clear(); - } - @Test public void doTestWithStart() { String dsl = - "// 查找使用了相同主演的两个导演\n" + "// ip在高危地区\n" + "GraphStructure {\n" + " (a:ABM.User)-[hasIp:acc2ip]->(ip:ABM.IP)\n" + "}\n" + "Rule {\n" + " \tR1(\"必选180天以内\"): hasIp.ipUse180dCnt >=2\n" - + " R2(\"必须是高危地区\"): ip.country in ['菲律宾','柬埔寨','老挝','日本','香港','台湾','泰国','澳门','越南','马来西亚','印度尼西亚']\n" + + " R2(\"必须是高危地区\"): ip.country in ['地址1','地址2','地址3']\n" + " result = rule_value(R2, true, false)\n" + "}\n" + "Action {\n" diff --git a/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/loader/TestFanxiqianGraphLoader.java b/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/loader/TestFanxiqianGraphLoader.java new file mode 100644 index 000000000..e0fc22061 --- /dev/null +++ b/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/loader/TestFanxiqianGraphLoader.java @@ -0,0 +1,43 @@ +/* + * 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.runner.local.loader; + +import com.antgroup.openspg.reasoner.common.graph.edge.IEdge; +import com.antgroup.openspg.reasoner.common.graph.property.IProperty; +import com.antgroup.openspg.reasoner.common.graph.vertex.IVertex; +import com.antgroup.openspg.reasoner.runner.local.load.graph.AbstractLocalGraphLoader; +import com.google.common.collect.Lists; +import java.util.List; + +public class TestFanxiqianGraphLoader extends AbstractLocalGraphLoader { + @Override + public List> genVertexList() { + return Lists.newArrayList( + constructionVertex("张三", "Test.User", "id", "张三", "name", "高级健康保险", "nightTrader", "3"), + constructionVertex( + "白领-医生", "Test.UserFeature", "id", "白领-医生", "name", "医生", "nightTrader", "3"), + constructionVertex( + "学生-就读中学", "Test.UserFeature", "id", "学生-就读中学", "name", "就读中学", "nightTrader", "3"), + constructionVertex( + "职业", "Test.TaxOfUserFeature", "id", "职业", "name", "职业", "nightTrader", "3"), + constructionVertex( + "学习阶段", "Test.TaxOfUserFeature", "id", "学习阶段", "name", "学习阶段", "nightTrader", "3")); + } + + @Override + public List> genEdgeList() { + return Lists.newArrayList( + constructionEdge("白领-医生", "newedge", "职业"), constructionEdge("学生-就读中学", "newedge", "学习阶段")); + } +} diff --git a/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/KgReasonerAddEdgeWithPropertyTest.java b/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/KgReasonerAddEdgeWithPropertyTest.java deleted file mode 100644 index 8127f2384..000000000 --- a/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/KgReasonerAddEdgeWithPropertyTest.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * 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.runner.local.main; - -import com.antgroup.openspg.reasoner.common.constants.Constants; -import com.antgroup.openspg.reasoner.common.graph.edge.IEdge; -import com.antgroup.openspg.reasoner.common.graph.property.IProperty; -import com.antgroup.openspg.reasoner.common.graph.vertex.IVertex; -import com.antgroup.openspg.reasoner.lube.catalog.Catalog; -import com.antgroup.openspg.reasoner.lube.catalog.impl.PropertyGraphCatalog; -import com.antgroup.openspg.reasoner.runner.local.LocalReasonerRunner; -import com.antgroup.openspg.reasoner.runner.local.load.graph.AbstractLocalGraphLoader; -import com.antgroup.openspg.reasoner.runner.local.model.LocalReasonerResult; -import com.antgroup.openspg.reasoner.runner.local.model.LocalReasonerTask; -import com.antgroup.openspg.reasoner.util.Convert2ScalaUtil; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.junit.Assert; -import org.junit.Test; - -public class KgReasonerAddEdgeWithPropertyTest { - @Test - public void testAddEdgeWithProperty() { - String dsl = - "Define (s:User)-[p:tradeInfo]->(o:User) {\n" - + " GraphStructure {\n" - + " (s:User)-[t:trade]->(o:User)\n" - + " } Rule {\n" - + " R1(\"交易时间在90天内\"): t.trade_time < now()\n" - + " trade_num(\"计算每个交易对象的交易次数\") = group(s,o).count(t)\n" - + " p.trade_num = trade_num\n" - + " }\n" - + "}\n" - + "\n" - + "Define (s:User)-[p:belongTo]->(o:CrowdType/`RepeatTradeUser`) {\n" - + " GraphStructure {\n" - + " (s:User)-[t:tradeInfo]->(u:User)\n" - + " } Rule {\n" - + " trade_user_count = group(s).count(u.id)\n" - + " R2(\"至少有3个交易对手\"): trade_user_count >= 3\n" - + " every_user_trade_more_then3 = group(s).countIf(t.trade_num > 3, t.trade_num)\n" - + " R3(\"每个交于对手交易大于3比\"): every_user_trade_more_then3 >= 1\n" - + " }\n" - + "}\n" - + "\n" - + "GraphStructure {\n" - + " (s:CrowdType/`RepeatTradeUser`)\n" - + "}\n" - + "Rule{\n" - + "}\n" - + "Action {\n" - + " get(s.id)\n" - + "}\n"; - - System.out.println(dsl); - LocalReasonerTask task = new LocalReasonerTask(); - task.setDsl(dsl); - - // add mock catalog - Map> schema = new HashMap<>(); - schema.put("User", Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("id"))); - schema.put("CrowdType", Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("id"))); - schema.put( - "User_trade_User", Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("trade_time"))); - Catalog catalog = new PropertyGraphCatalog(Convert2ScalaUtil.toScalaImmutableMap(schema)); - catalog.init(); - task.setCatalog(catalog); - - task.setGraphLoadClass( - "com.antgroup.openspg.reasoner.runner.local.main.KgReasonerAddEdgeWithPropertyTest$GraphLoader"); - - // enable subquery - Map params = new HashMap<>(); - params.put(Constants.SPG_REASONER_LUBE_SUBQUERY_ENABLE, true); - task.setParams(params); - - LocalReasonerRunner runner = new LocalReasonerRunner(); - LocalReasonerResult result = runner.run(task); - - // only u1 - Assert.assertEquals(1, result.getRows().size()); - Assert.assertEquals("u1", result.getRows().get(0)[0]); - } - - public static class GraphLoader extends AbstractLocalGraphLoader { - - @Override - public List> genVertexList() { - return Lists.newArrayList( - constructionVertex("u1", "User"), - constructionVertex("u2", "User"), - constructionVertex("u3", "User"), - constructionVertex("u4", "User"), - constructionVertex("RepeatTradeUser", "CrowdType")); - } - - @Override - public List> genEdgeList() { - return Lists.newArrayList( - constructionVersionEdge("u1", "trade", "u2", 1, "trade_time", 1), - constructionVersionEdge("u1", "trade", "u2", 2, "trade_time", 2), - constructionVersionEdge("u1", "trade", "u2", 3, "trade_time", 3), - constructionVersionEdge("u1", "trade", "u3", 1, "trade_time", 1), - constructionVersionEdge("u1", "trade", "u3", 2, "trade_time", 2), - constructionVersionEdge("u1", "trade", "u3", 3, "trade_time", 3), - constructionVersionEdge("u1", "trade", "u3", 4, "trade_time", 4), - constructionVersionEdge("u1", "trade", "u4", 1, "trade_time", 1), - constructionVersionEdge("u1", "trade", "u4", 2, "trade_time", 2), - constructionVersionEdge("u1", "trade", "u4", 3, "trade_time", 3), - constructionVersionEdge("u2", "trade", "u3", 1, "trade_time", 1), - constructionVersionEdge("u2", "trade", "u3", 2, "trade_time", 2), - constructionVersionEdge("u2", "trade", "u3", 3, "trade_time", 3), - constructionVersionEdge("u2", "trade", "u4", 1, "trade_time", 1), - constructionVersionEdge("u2", "trade", "u4", 2, "trade_time", 2), - constructionVersionEdge("u2", "trade", "u4", 3, "trade_time", 3)); - } - } -} diff --git a/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/KgReasonerZijinLocalTest.java b/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/KgReasonerZijinLocalTest.java index 176660052..32593b805 100644 --- a/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/KgReasonerZijinLocalTest.java +++ b/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/KgReasonerZijinLocalTest.java @@ -19,8 +19,6 @@ import com.antgroup.openspg.reasoner.common.graph.vertex.IVertex; import com.antgroup.openspg.reasoner.lube.catalog.Catalog; import com.antgroup.openspg.reasoner.lube.catalog.impl.PropertyGraphCatalog; -import com.antgroup.openspg.reasoner.recorder.DefaultRecorder; -import com.antgroup.openspg.reasoner.runner.ConfigKey; import com.antgroup.openspg.reasoner.runner.local.LocalReasonerRunner; import com.antgroup.openspg.reasoner.runner.local.load.graph.AbstractLocalGraphLoader; import com.antgroup.openspg.reasoner.runner.local.model.LocalReasonerResult; @@ -37,152 +35,6 @@ public class KgReasonerZijinLocalTest { - @Test - public void test41() { - String dsl = - "//异常借贷行为\n" - + "GraphStructure {\n" - + "A[AMLz50.BlackCompanyRelated]\n" - + "}\n" - + "Rule\n" - + "{\t\n" - + "R1(\"频繁花呗借款\") = rule_value(A.explainStrategyList rlike 'HuaBeiMultiLending', true, false)\n" - + "R2(\"花呗提前还款\") = rule_value(A.explainStrategyList rlike 'HuaBeiMultiPrepay', true, false)\n" - + "R3(\"花呗逾期未还\") = rule_value(A.explainStrategyList rlike 'HuaBeiCreditOverdue', true, false)\n" - + "\n" - + "result = R1 or R2 or R3\n" - + "\t\tr5 = rule_value(R3, \"花呗逾期未还\", \"\")\n" - + "\t\tr4 = rule_value(R1, \"频繁借款\", r5)\n" - + "\t\tr3 = rule_value(R1 && R3, \"频繁借款,花呗逾期未还\", r4)\n" - + " r2 = rule_value(R1 && R2, \"频繁借款后提前还款\", r3)\n" - + " r1 = rule_value(R1 && R2 && R3, \"频繁借款后提前还款,花呗逾期未还\", r2)\n" - + "}\n" - + "Action {\n" - + "\tget(A.id,result,r1)\n" - + "}"; - System.out.println(dsl); - LocalReasonerTask task = new LocalReasonerTask(); - task.setDsl(dsl); - // add mock catalog - Map> schema = new HashMap<>(); - schema.put( - "AMLz50.BlackCompanyRelated", - Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("explainStrategyList"))); - Catalog catalog = new PropertyGraphCatalog(Convert2ScalaUtil.toScalaImmutableMap(schema)); - catalog.init(); - task.setCatalog(catalog); - task.setGraphLoadClass( - "com.antgroup.openspg.reasoner.runner.local.main.KgReasonerZijinLocalTest$GraphLoader41"); - // enable subquery - Map params = new HashMap<>(); - params.put(ConfigKey.KG_REASONER_BINARY_PROPERTY, "false"); - params.put(Constants.SPG_REASONER_LUBE_SUBQUERY_ENABLE, true); - task.setParams(params); - task.setExecutorTimeoutMs(99999999999999999L); - task.setExecutionRecorder(new DefaultRecorder()); - LocalReasonerRunner runner = new LocalReasonerRunner(); - LocalReasonerResult result = runner.run(task); - System.out.println(task.getExecutionRecorder().toReadableString()); - // only u1 - Assert.assertEquals(1, result.getRows().size()); - } - - public static class GraphLoader41 extends AbstractLocalGraphLoader { - @Override - public List> genVertexList() { - return Lists.newArrayList(constructionVertex("SA", "AMLz50.BlackCompanyRelated")); - } - - @Override - public List> genEdgeList() { - return Lists.newArrayList(); - } - } - - @Test - public void test3() { - String dsl = - "// 当月交易量\n" - + "Define (s:CustFundKG.Account)-[p:cur_month_num]->(o:Int) {\n" - + " GraphStructure {\n" - + " (u:CustFundKG.Account)-[t:accountFundContact]-(s)\n" - + " }\n" - + "\tRule {\n" - + " \tR1(\"当月交易量\"): date_diff(from_unix_time(now(), 'yyyyMMdd'),t.transDate) <= 30\n" - + " o = group(s).count(t.transDate)\n" - + " }\n" - + "}\n" - + "// 次月交易量\n" - + "Define (s:CustFundKG.Account)-[p:last_month_num]->(o:Int) {\n" - + " GraphStructure {\n" - + " (u:CustFundKG.Account)-[t:accountFundContact]-(s)\n" - + " }\n" - + "\tRule {\n" - + " \tdate_delta = date_diff(from_unix_time(now(), 'yyyyMMdd'),t.transDate)\n" - + " \tR1(\"次月交易量\"): date_delta > 30 && date_delta <=60\n" - + " o = group(s).count(t.transDate)\n" - + " }\n" - + "}\n" - + "\n" - + "// 倍数\n" - + "Define (s:CustFundKG.Account)-[p:trans_multiple]->(o:Float) {\n" - + "\t\tGraphStructure {\n" - + " (s)\n" - + " }\n" - + "\tRule {\n" - + " \tcur_month = rule_value(s.cur_month_num==null, 0.0, s.cur_month_num*1.0)\n" - + " last_month = rule_value(s.last_month_num == null, 1, s.last_month_num)\n" - + " \tmultiple = cur_month / last_month\n" - + " o = multiple\n" - + " }\n" - + "}\n" - + "\n" - + "Define (s:CustFundKG.Account)-[p:is_trans_raise_more]->(o:Boolean) {\n" - + "\t\tGraphStructure {\n" - + " (s)\n" - + " }\n" - + "\tRule {\n" - + " \to = rule_value(s.trans_multiple >= 3, true, false)\n" - + " }\n" - + "}\n" - + "\n" - + "GraphStructure {\n" - + " (s:CustFundKG.Account)\n" - + "}\n" - + "Rule {\n" - + "}\n" - + "Action {\n" - + "\tget(s.id, s.is_trans_raise_more, s.trans_multiple, s.last_month_num, s.cur_month_num)\n" - + "}\n"; - LocalReasonerTask task = new LocalReasonerTask(); - task.setStartIdList(Lists.newArrayList(new Tuple2<>("2088xx3", "CustFundKG.Account"))); - - task.setExecutorTimeoutMs(60 * 1000 * 100); - task.setDsl(dsl); - - Map> schema = new HashMap<>(); - schema.put("CustFundKG.Account", Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("id"))); - schema.put( - "CustFundKG.Account_accountFundContact_CustFundKG.Account", - Convert2ScalaUtil.toScalaImmutableSet(Sets.newHashSet("transDate"))); - Catalog catalog = new PropertyGraphCatalog(Convert2ScalaUtil.toScalaImmutableMap(schema)); - catalog.init(); - task.setCatalog(catalog); - - task.setGraphLoadClass( - "com.antgroup.openspg.reasoner.runner.local.main.KgReasonerZijinLocalTest$GraphLoader"); - - // enable subquery - Map params = new HashMap<>(); - params.put(Constants.SPG_REASONER_LUBE_SUBQUERY_ENABLE, true); - task.setParams(params); - - LocalReasonerRunner runner = new LocalReasonerRunner(); - LocalReasonerResult result = runner.run(task); - System.out.println(result); - Assert.assertEquals(1, result.getRows().size()); - } - @Test public void test4() { String dsl = @@ -247,56 +99,4 @@ public List> genEdgeList() { constructionEdge("2088xx3", "accountFundContact", "2088xx4", "transDate", "20230801")); } } - - public static class GraphLoader2 extends AbstractLocalGraphLoader { - - @Override - public List> genVertexList() { - return Lists.newArrayList( - constructionVertex("day", "AttributePOC.TracebackDay"), - constructionVertex( - "s", "AttributePOC.BrinsonAttribute", "factorType", "total", "factorValue", 0.01), - constructionVertex( - "stock", - "AttributePOC.BrinsonAttribute", - "factorType", - "stock", - "factorValue", - 0.004), - constructionVertex( - "trade", - "AttributePOC.BrinsonAttribute", - "factorType", - "trade", - "factorValue", - -0.005), - constructionVertex( - "market", - "AttributePOC.BrinsonAttribute", - "factorType", - "market", - "factorValue", - 0.006), - constructionVertex( - "cluster", - "AttributePOC.BrinsonAttribute", - "factorType", - "cluster", - "factorValue", - -0.003), - constructionVertex("市场贡献大", "AttributePOC.TaxonomyOfBrinsonAttribute"), - constructionVertex("选股贡献大", "AttributePOC.TaxonomyOfBrinsonAttribute"), - constructionVertex("交易贡献大", "AttributePOC.TaxonomyOfBrinsonAttribute")); - } - - @Override - public List> genEdgeList() { - return Lists.newArrayList( - constructionEdge("day", "day", "s"), - constructionEdge("s", "factorValue", "stock"), - constructionEdge("s", "factorValue", "trade"), - constructionEdge("s", "factorValue", "market"), - constructionEdge("s", "factorValue", "cluster")); - } - } } diff --git a/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/group/concept/GroupConceptTest.java b/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/group/concept/GroupConceptTest.java index ef5116fa0..908c34757 100644 --- a/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/group/concept/GroupConceptTest.java +++ b/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/group/concept/GroupConceptTest.java @@ -58,7 +58,7 @@ public List> genVertexList() { constructionVertex("le2_2", "LoginEvent", "eventTime", "1640970059"), constructionVertex("le2_3", "LoginEvent", "eventTime", "1700353023"), constructionVertex("参加活动", "ActivityInfo"), - constructionVertex("参加活动/五福", "ActivityInfo"), + constructionVertex("参加活动/活动1", "ActivityInfo"), constructionVertex("参加活动/红包", "ActivityInfo"), constructionVertex("登录来源渠道", "LoginSource"), constructionVertex("主动登端", "LoginSource"), @@ -73,21 +73,21 @@ public List> genVertexList() { public List> genEdgeList() { return Lists.newArrayList( // hypernym - constructionEdge("参加活动/五福", "isA", "参加活动"), + constructionEdge("参加活动/活动1", "isA", "参加活动"), constructionEdge("参加活动/红包", "isA", "参加活动"), constructionEdge("主动登端", "isA", "登录来源渠道"), constructionEdge("渠道拉动登端", "isA", "登录来源渠道"), constructionEdge("主动登端-账户余额查询", "isA", "主动登端"), constructionEdge("e1_1", "subject", "u1"), - constructionEdge("e1_1", "activityName", "参加活动/五福"), + constructionEdge("e1_1", "activityName", "参加活动/活动1"), constructionEdge("e1_2", "subject", "u1"), - constructionEdge("e1_2", "activityName", "参加活动/五福"), + constructionEdge("e1_2", "activityName", "参加活动/活动1"), constructionEdge("e1_3", "subject", "u1"), constructionEdge("e1_3", "activityName", "参加活动/红包"), constructionEdge("e2_1", "subject", "u2"), - constructionEdge("e2_1", "activityName", "参加活动/五福"), + constructionEdge("e2_1", "activityName", "参加活动/活动1"), constructionEdge("e2_2", "subject", "u2"), - constructionEdge("e2_2", "activityName", "参加活动/五福"), + constructionEdge("e2_2", "activityName", "参加活动/活动1"), constructionEdge("le1_1", "subject", "u1"), constructionEdge("le1_1", "sourceClassification", "主动登端-账户余额查询"), constructionEdge("le1_2", "subject", "u1"), @@ -117,7 +117,7 @@ public void groupTest1() { LocalRunnerTestFactory.runTest( "Define (s:User) -[p:belongTo]-> (o:ActivityInfo) {\n" + " GraphStructure {\n" - + " (s) <-[:subject]- (e:InterviewEvent) -[:concept_edge_expand(e, 'activityName', ['参加活动/五福'], \n" + + " (s) <-[:subject]- (e:InterviewEvent) -[:concept_edge_expand(e, 'activityName', ['参加活动/活动1'], \n" + "'ActivityInfo')]-> (o)\n" + " }\n" + "Rule {\n" @@ -174,7 +174,7 @@ public void assertResult(LocalReasonerResult result) { } rstMap.put(key.toString(), String.valueOf(strings[5])); } - Assert.assertEquals("2", rstMap.get(",参加活动/五福,null,null,null,20220101")); + Assert.assertEquals("2", rstMap.get(",参加活动/活动1,null,null,null,20220101")); } }, this.params); diff --git a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/pattern/PatternMatcher.java b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/pattern/PatternMatcher.java index 7b85badfb..0566f738f 100644 --- a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/pattern/PatternMatcher.java +++ b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/pattern/PatternMatcher.java @@ -52,6 +52,7 @@ public class PatternMatcher implements Serializable { private final String taskId; private final GraphState graphState; private long initTime; + private boolean debugEnable = false; public PatternMatcher(String taskId, GraphState graphState) { this.taskId = taskId; @@ -63,6 +64,15 @@ public void resetInitTime() { this.initTime = System.currentTimeMillis(); } + /** + * set enable flag + * + * @param debugEnable + */ + public void setDebugEnable(boolean debugEnable) { + this.debugEnable = debugEnable; + } + /** * vertex center pattern match * @@ -92,6 +102,9 @@ public KgGraph patternMatch( } IVertex vertex = graphState.getVertex(id, endVersion, vertexRule); if (null == vertex) { + if (this.debugEnable) { + log.warn("PatternMatcher patternMatch get id=" + id.toString() + " is null"); + } return null; } @@ -100,7 +113,16 @@ public KgGraph patternMatch( // check root types PatternElement patternElement = pattern.root(); - if (!patternElement.typeNames().contains(RunnerUtil.getVertexType(vertex))) { + if (!patternElement.getMetaTypeNames().contains(RunnerUtil.getVertexType(vertex))) { + if (this.debugEnable) { + log.warn( + "PatternMatcher patternMatch meta type match failed id=" + + id.toString() + + " need=" + + patternElement.getMetaTypeNames() + + ", value is=" + + RunnerUtil.getVertexType(vertex)); + } return null; } @@ -108,7 +130,7 @@ public KgGraph patternMatch( Map vertexContext = RunnerUtil.vertexContext(vertex, pattern.root().alias()); if (!rootVertexRuleList.isEmpty() && !RuleRunner.getInstance().check(vertexContext, rootVertexRuleList, this.taskId)) { - if (DebugVertexIdSet.DEBUG_VERTEX_ID_SET.contains(vertex.getId())) { + if (DebugVertexIdSet.DEBUG_VERTEX_ID_SET.contains(vertex.getId()) || this.debugEnable) { log.info( "PatternMatch check vertex rule false, vertexContext=" + JSON.toJSONString(vertexContext) @@ -159,6 +181,19 @@ public KgGraph patternMatch( Sets.newHashSet(edgeType), direction, edgeTypeRuleMap); + if (this.debugEnable) { + log.warn( + "PatternMatcher patternMatch get edge id=" + + id.toString() + + " edgeType=" + + edgeType + + ", direction=" + + direction + + " edgeTypeRuleMap=" + + JSON.toJSONString(edgeTypeRuleMap) + + " edges=" + + JSON.toJSONString(edges)); + } edgeList.addAll(edges); } } @@ -187,6 +222,15 @@ public boolean test(IEdge edge) { return !validVertexIdSet.contains(edge.getTargetId()); } }); + if (this.debugEnable) { + log.warn( + "PatternMatcher patternMatch validVertexIdSet id=" + + id.toString() + + " willMatchEdgeList=" + + JSON.toJSONString(willMatchEdgeList) + + " validVertexIdSet=" + + JSON.toJSONString(validVertexIdSet)); + } } // check dst vertex rule list @@ -202,6 +246,15 @@ public boolean test(IEdge e) { return !RuleRunner.getInstance().check(context, dstVertexRuleList, taskId); } }); + if (this.debugEnable) { + log.warn( + "PatternMatcher patternMatch dstVertexRuleList id=" + + id.toString() + + " willMatchEdgeList=" + + JSON.toJSONString(willMatchEdgeList) + + " dstVertexRuleList=" + + JSON.toJSONString(dstVertexRuleList)); + } } List> validEdges = @@ -209,7 +262,7 @@ public boolean test(IEdge e) { vertexContext, willMatchEdgeList, patternConnection, pattern, edgeRuleMap, limit); if (CollectionUtils.isEmpty(validEdges)) { // one edge pattern connection no match - if (DebugVertexIdSet.DEBUG_VERTEX_ID_SET.contains(vertex.getId())) { + if (DebugVertexIdSet.DEBUG_VERTEX_ID_SET.contains(vertex.getId()) || this.debugEnable) { log.info( "PatternMatch edge not match, vertexContext=" + JSON.toJSONString(vertexContext) @@ -282,7 +335,7 @@ private boolean isEdgeMatch( // edge target type match if (!pattern .getNode(patternConnection.target()) - .typeNames() + .getMetaTypeNames() .contains(RunnerUtil.getEdgeTargetType(edge))) { return false; } @@ -290,7 +343,7 @@ private boolean isEdgeMatch( // edge source type match if (!pattern .getNode(patternConnection.source()) - .typeNames() + .getMetaTypeNames() .contains(RunnerUtil.getEdgeSourceType(edge))) { return false; } diff --git a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/DefaultRecorder.java b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/DefaultRecorder.java index 8ec7298ff..240d60dde 100644 --- a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/DefaultRecorder.java +++ b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/DefaultRecorder.java @@ -70,10 +70,7 @@ public void stageResultWithDesc(String stage, long result, String finishDescribe @Override public void stageResultWithDetail( - String stage, - long result, - Map> runtimeDetail, - List relateRules) { + String stage, long result, Map runtimeDetail, List relateRules) { SubAction nowAction = actionStack.peek(); nowAction.getSubActionList().add(new SampleAction(stage, result, runtimeDetail, relateRules)); } @@ -83,7 +80,7 @@ public void stageResultWithDescAndDetail( String stage, long result, String finishDescribe, - Map> runtimeDetail, + Map runtimeDetail, List relateRules) { SubAction nowAction = actionStack.peek(); nowAction diff --git a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/EmptyRecorder.java b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/EmptyRecorder.java index 72c40f5b0..af0fa33a8 100644 --- a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/EmptyRecorder.java +++ b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/EmptyRecorder.java @@ -45,16 +45,13 @@ public void stageResultWithDesc(String stage, long result, String finishDescribe @Override public void stageResultWithDetail( - String stage, - long result, - Map> runtimeDetail, - List relateRules) {} + String stage, long result, Map runtimeDetail, List relateRules) {} @Override public void stageResultWithDescAndDetail( String stage, long result, String finishDescribe, - Map> runtimeDetail, + Map runtimeDetail, List relateRules) {} } diff --git a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/IExecutionRecorder.java b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/IExecutionRecorder.java index b00558652..7c85d85b8 100644 --- a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/IExecutionRecorder.java +++ b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/IExecutionRecorder.java @@ -44,16 +44,13 @@ public interface IExecutionRecorder { /** record result num, like filer, expendInto */ void stageResultWithDetail( - String stage, - long result, - Map> runtimeDetail, - List relateRules); + String stage, long result, Map runtimeDetail, List relateRules); /** finish */ void stageResultWithDescAndDetail( String stage, long result, String finishDescribe, - Map> runtimeDetail, + Map runtimeDetail, List relateRules); } diff --git a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/DebugInfoWithRule.java b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/DebugInfoWithRule.java new file mode 100644 index 000000000..6c2a89f39 --- /dev/null +++ b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/DebugInfoWithRule.java @@ -0,0 +1,45 @@ +/* + * 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.recorder.action; + +import com.antgroup.openspg.reasoner.lube.common.rule.Rule; + +/** + * @author peilong.zpl + * @version $Id: DebugInfoWithRule.java, v 0.1 2024-06-06 13:37 peilong.zpl Exp $$ + */ +public class DebugInfoWithRule { + private Rule rule; + private String runtimeValue; + + public DebugInfoWithRule(Rule rule, String runtimeValue) { + this.rule = rule; + this.runtimeValue = runtimeValue; + } + + public Rule getRule() { + return rule; + } + + public void setRule(Rule rule) { + this.rule = rule; + } + + public String getRuntimeValue() { + return runtimeValue; + } + + public void setRuntimeValue(String runtimeValue) { + this.runtimeValue = runtimeValue; + } +} diff --git a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/DebugInfoWithStartId.java b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/DebugInfoWithStartId.java index e72fe6502..10dd5316b 100644 --- a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/DebugInfoWithStartId.java +++ b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/DebugInfoWithStartId.java @@ -13,7 +13,6 @@ package com.antgroup.openspg.reasoner.recorder.action; import com.antgroup.openspg.reasoner.common.graph.vertex.IVertexId; -import com.antgroup.openspg.reasoner.lube.common.rule.Rule; import com.antgroup.openspg.reasoner.warehouse.utils.WareHouseUtils; import java.util.ArrayList; import java.util.HashMap; @@ -27,8 +26,8 @@ */ public class DebugInfoWithStartId { private IVertexId vertexId; - private List hitRules = new ArrayList<>(); - private List failedRules = new ArrayList<>(); + private List hitRules = new ArrayList<>(); + private List failedRules = new ArrayList<>(); public IVertexId getVertexId() { return vertexId; @@ -38,20 +37,22 @@ public Map toJsonObj() { Map result = new HashMap<>(); List> hitRuleInfos = new ArrayList<>(); - for (Rule r : hitRules) { + for (DebugInfoWithRule r : hitRules) { Map hitRuleInfoDetail = new HashMap<>(); - hitRuleInfoDetail.put("ruleName", r.getName()); - hitRuleInfoDetail.put("ruleValue", StringUtils.join(WareHouseUtils.getRuleList(r), ",")); - hitRuleInfoDetail.put("hitValue", ""); + hitRuleInfoDetail.put("ruleName", r.getRule().getName()); + hitRuleInfoDetail.put( + "ruleValue", StringUtils.join(WareHouseUtils.getRuleList(r.getRule()), ",")); + hitRuleInfoDetail.put("hitValue", r.getRuntimeValue()); hitRuleInfos.add(hitRuleInfoDetail); } result.put("hitRule", hitRuleInfos); List> failedRuleInfos = new ArrayList<>(); - for (Rule r : failedRules) { + for (DebugInfoWithRule r : failedRules) { Map failedRuleInfoDetail = new HashMap<>(); - failedRuleInfoDetail.put("ruleName", r.getName()); - failedRuleInfoDetail.put("ruleValue", StringUtils.join(WareHouseUtils.getRuleList(r), ",")); - failedRuleInfoDetail.put("hitValue", ""); + failedRuleInfoDetail.put("ruleName", r.getRule().getName()); + failedRuleInfoDetail.put( + "ruleValue", StringUtils.join(WareHouseUtils.getRuleList(r.getRule()), ",")); + failedRuleInfoDetail.put("hitValue", r.getRuntimeValue()); failedRuleInfos.add(failedRuleInfoDetail); } result.put("failedRule", failedRuleInfos); @@ -70,19 +71,19 @@ public void setVertexId(IVertexId vertexId) { this.vertexId = vertexId; } - public List getHitRules() { + public List getHitRules() { return hitRules; } - public void setHitRules(List hitRules) { + public void setHitRules(List hitRules) { this.hitRules = hitRules; } - public List getFailedRules() { + public List getFailedRules() { return failedRules; } - public void setFailedRules(List failedRules) { + public void setFailedRules(List failedRules) { this.failedRules = failedRules; } } diff --git a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/SampleAction.java b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/SampleAction.java index 2d50d6b36..3fd113633 100644 --- a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/SampleAction.java +++ b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/SampleAction.java @@ -28,13 +28,14 @@ public class SampleAction extends AbstractAction { public static final String PASS_START_ID_KEY = "pass"; public static final String FAILED_START_ID_KEY = "failed"; + public static final String RULE_RUNTIME_VALUE = "rule_runtime_value"; private final List relateRules; private final String describe; private final Long num; private final String finishDescribe; - private final Map> runtimeDetail; + private final Map runtimeDetail; public SampleAction(String describe, long num) { super(System.currentTimeMillis()); @@ -46,10 +47,7 @@ public SampleAction(String describe, long num) { } public SampleAction( - String describe, - long num, - Map> runtimeDetail, - List relateRules) { + String describe, long num, Map runtimeDetail, List relateRules) { super(System.currentTimeMillis()); this.describe = describe; this.num = num; @@ -62,7 +60,7 @@ public SampleAction( String describe, long num, String finishDescribe, - Map> runtimeDetail, + Map runtimeDetail, List relateRules) { super(System.currentTimeMillis()); this.describe = describe; @@ -90,16 +88,31 @@ public SampleAction(String describe, Long num, long time) { this.relateRules = null; } + private String getRuntimeValue(IVertexId iVertexId) { + Map runtimeValueMap = getRuleRuntimeValue(); + return runtimeValueMap.computeIfAbsent(iVertexId, k -> ""); + } + + private List generateDebugInfoRuleSet(IVertexId iVertexId) { + String value = getRuntimeValue(iVertexId); + List res = new ArrayList<>(); + for (Rule rule : this.relateRules) { + res.add(new DebugInfoWithRule(rule, value)); + } + return res; + } + @Override public Map getRuleRuntimeInfo() { Map startIdMap = new HashMap<>(); if (this.runtimeDetail == null) { return startIdMap; } - for (IVertexId passId : getRuntimeDetail().get(PASS_START_ID_KEY)) { + + for (IVertexId passId : getPassStartID()) { DebugInfoWithStartId debugInfo = new DebugInfoWithStartId(); debugInfo.setVertexId(passId); - debugInfo.getHitRules().addAll(this.relateRules); + debugInfo.getHitRules().addAll(generateDebugInfoRuleSet(passId)); startIdMap.put(passId, debugInfo); } return startIdMap; @@ -121,10 +134,25 @@ public String getFinishDescribe() { return finishDescribe; } - public Map> getRuntimeDetail() { + public Map getRuntimeDetail() { return runtimeDetail; } + public List getPassStartID() { + return (List) + this.runtimeDetail.computeIfAbsent(PASS_START_ID_KEY, k -> new ArrayList<>()); + } + + public List getFailedStartID() { + return (List) + this.runtimeDetail.computeIfAbsent(FAILED_START_ID_KEY, k -> new ArrayList<>()); + } + + public Map getRuleRuntimeValue() { + return (Map) + this.runtimeDetail.computeIfAbsent(RULE_RUNTIME_VALUE, k -> new HashMap<>()); + } + @Override public String toString() { SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss.SSS"); @@ -133,15 +161,23 @@ public String toString() { StringBuilder failedIds = new StringBuilder(); if (runtimeDetail != null) { passIds.append(", [pass]{"); - for (IVertexId vertexId : - this.runtimeDetail.computeIfAbsent(PASS_START_ID_KEY, k -> new ArrayList<>())) { - passIds.append(" ").append(vertexId.toString()); + for (IVertexId vertexId : getPassStartID()) { + passIds + .append(" ") + .append(vertexId.toString()) + .append("(") + .append(getRuntimeValue(vertexId)) + .append(")"); } passIds.append("}"); failedIds.append(", [failed]{"); - for (IVertexId vertexId : - this.runtimeDetail.computeIfAbsent(FAILED_START_ID_KEY, k -> new ArrayList<>())) { - failedIds.append(" ").append(vertexId.toString()); + for (IVertexId vertexId : getFailedStartID()) { + failedIds + .append(" ") + .append(vertexId.toString()) + .append("(") + .append(getRuntimeValue(vertexId)) + .append(")"); } failedIds.append("}"); } diff --git a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/SubAction.java b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/SubAction.java index e30255ed5..fb86ec60e 100644 --- a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/SubAction.java +++ b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/recorder/action/SubAction.java @@ -34,6 +34,9 @@ public SubAction(String describe) { public Map getRuleRuntimeInfo() { Map vertexIdMap = new HashMap<>(); for (AbstractAction action : subActionList) { + if (!(action instanceof SampleAction)) { + continue; + } Map startIds = action.getRuleRuntimeInfo(); for (IVertexId d : startIds.keySet()) { if (vertexIdMap.containsKey(d)) { 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 cfe39d19e..d0e6d68a5 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 @@ -635,6 +635,9 @@ public static List loadCsvFile(String file) { /** get connection set from pattern */ public static Set getConnectionSet(Pattern schema) { Set connectionSet = new HashSet<>(); + if (schema.topology() == null || schema.topology().isEmpty()) { + return connectionSet; + } for (String pe : JavaConversions.mapAsJavaMap(schema.topology()).keySet()) { Set partConnectionSet = JavaConversions.setAsJavaSet(schema.topology().get(pe).get()); diff --git a/reasoner/runner/runner-common/src/main/scala/com/antgroup/openspg/reasoner/util/LoaderUtil.scala b/reasoner/runner/runner-common/src/main/scala/com/antgroup/openspg/reasoner/util/LoaderUtil.scala index 525ae2189..bf9aeba72 100644 --- a/reasoner/runner/runner-common/src/main/scala/com/antgroup/openspg/reasoner/util/LoaderUtil.scala +++ b/reasoner/runner/runner-common/src/main/scala/com/antgroup/openspg/reasoner/util/LoaderUtil.scala @@ -13,30 +13,22 @@ package com.antgroup.openspg.reasoner.util +import scala.collection.JavaConverters._ +import scala.collection.mutable + import com.antgroup.openspg.reasoner.common.constants.Constants import com.antgroup.openspg.reasoner.common.graph.edge.{Direction, SPO} import com.antgroup.openspg.reasoner.common.types.KTString +import com.antgroup.openspg.reasoner.common.utils.LabelTypeUtils import com.antgroup.openspg.reasoner.lube.catalog.{Catalog, SemanticPropertyGraph} import com.antgroup.openspg.reasoner.lube.catalog.struct.Field import com.antgroup.openspg.reasoner.lube.common.expr.VString -import com.antgroup.openspg.reasoner.lube.common.pattern.{ - EdgePattern, - LinkedPatternConnection, - PartialGraphPattern, - Pattern, - PatternConnection -} +import com.antgroup.openspg.reasoner.lube.common.pattern.{EdgePattern, LinkedPatternConnection, PartialGraphPattern, Pattern, PatternConnection} import com.antgroup.openspg.reasoner.lube.common.rule.Rule import com.antgroup.openspg.reasoner.lube.logical.{EdgeVar, NodeVar, SolvedModel, Var} import com.antgroup.openspg.reasoner.lube.logical.PatternOps.PatternOps import com.antgroup.openspg.reasoner.lube.logical.operators._ -import com.antgroup.openspg.reasoner.warehouse.common.config.{ - EdgeLoaderConfig, - GraphLoaderConfig, - VertexLoaderConfig -} -import scala.collection.JavaConverters._ -import scala.collection.mutable +import com.antgroup.openspg.reasoner.warehouse.common.config.{EdgeLoaderConfig, GraphLoaderConfig, VertexLoaderConfig} object LoaderUtil { @@ -89,7 +81,7 @@ object LoaderUtil { field match { case nodeVar: NodeVar => for (typeName <- solvedModel.getTypes(nodeVar.name)) { - val node = logicalPlan.graph.getNode(typeName.split("/")(0)) + val node = logicalPlan.graph.getNode(LabelTypeUtils.getMetaType(typeName)) if (node.resolved) { allVertexSet.add(node.typeName) } @@ -491,7 +483,7 @@ object LoaderUtil { field match { case nodeVar: NodeVar => for (typeName <- solvedModel.getTypes(field.name)) { - val node = logicalPlan.graph.getNode(typeName.split("/")(0)) + val node = logicalPlan.graph.getNode(LabelTypeUtils.getMetaType(typeName)) if (node.resolved) { val vertexLoaderConfig = new VertexLoaderConfig() if (typesAllowIsolateVertex.contains(node.typeName)) { @@ -520,7 +512,8 @@ object LoaderUtil { if (graph.containsEdge(typeName)) { val edge = logicalPlan.graph.getEdge(typeName) if (edge.resolved) { - val edgeTypeName = edge.startNode + "_" + edge.typeName + "_" + edge.endNode + val edgeTypeName = LabelTypeUtils.getMetaType(edge.startNode) + + "_" + edge.typeName + "_" + LabelTypeUtils.getMetaType(edge.endNode) val edgeLoaderConfig = new EdgeLoaderConfig() edgeLoaderConfig.setEdgeType(edgeTypeName) edgeLoaderConfig.setNeedProperties( 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 569d1f5c5..47cdce448 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 @@ -14,7 +14,9 @@ package com.antgroup.openspg.reasoner.warehouse.utils; import com.antgroup.openspg.reasoner.lube.common.expr.Expr; +import com.antgroup.openspg.reasoner.lube.common.graph.IREdge; import com.antgroup.openspg.reasoner.lube.common.graph.IRField; +import com.antgroup.openspg.reasoner.lube.common.graph.IRNode; import com.antgroup.openspg.reasoner.lube.common.pattern.Connection; import com.antgroup.openspg.reasoner.lube.common.pattern.Pattern; import com.antgroup.openspg.reasoner.lube.common.pattern.PatternElement; @@ -181,6 +183,29 @@ public static List getRuleList(Rule rule) { return Lists.newArrayList(JavaConversions.asJavaIterable(EXPR_TRANSFORMER.transform(rule))); } + /** get all ir filed * */ + public static List>> getRuleUsedAliasEle(Rule rule) { + List irFieldList = + JavaConversions.seqAsJavaList(RuleUtils.getAllInputFieldInRule(rule, null, null)); + List>> allFiledList = new ArrayList<>(); + for (IRField irField : irFieldList) { + if (irField instanceof IRNode) { + allFiledList.add( + new Tuple2>( + irField.name(), + Lists.newArrayList( + JavaConversions.seqAsJavaList(((IRNode) irField).fields().toSeq())))); + } else if (irField instanceof IREdge) { + allFiledList.add( + new Tuple2>( + irField.name(), + Lists.newArrayList( + JavaConversions.seqAsJavaList(((IREdge) irField).fields().toSeq())))); + } + } + return allFiledList; + } + /** transform rule to qlexpress */ public static Tuple2> getRuleListWithAlias(Rule rule) { List ruleList = From 25970d4fb300e33ab7331657d5ad52270d853aca Mon Sep 17 00:00:00 2001 From: wenchengyao Date: Mon, 1 Jul 2024 12:06:23 +0800 Subject: [PATCH 24/29] fix(reasoner): fix bugs in cast_type (#276) --- .../com/antgroup/openspg/reasoner/udf/builtin/udf/Cast.java | 3 ++- .../java/com/antgroup/openspg/reasoner/udf/test/UdfTest.java | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/builtin/udf/Cast.java b/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/builtin/udf/Cast.java index 65fe32965..ad0a9937b 100644 --- a/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/builtin/udf/Cast.java +++ b/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/builtin/udf/Cast.java @@ -14,6 +14,7 @@ package com.antgroup.openspg.reasoner.udf.builtin.udf; import com.antgroup.openspg.reasoner.udf.model.UdfDefine; +import java.math.BigDecimal; public class Cast { @UdfDefine(name = "cast_type", compatibleName = "Cast") @@ -25,7 +26,7 @@ public Object cast(Object op1, Object targetType) { if ("long".equalsIgnoreCase(castType) || "bigint".equalsIgnoreCase(castType) || "int".equalsIgnoreCase(castType)) { - return Long.valueOf(opdata1); + return Long.valueOf(new BigDecimal(opdata1).longValueExact()); } else if ("double".equalsIgnoreCase(castType) || "float".equalsIgnoreCase(castType)) { return Double.valueOf(opdata1); } else { diff --git a/reasoner/udf/src/test/java/com/antgroup/openspg/reasoner/udf/test/UdfTest.java b/reasoner/udf/src/test/java/com/antgroup/openspg/reasoner/udf/test/UdfTest.java index c6112af3d..d47cafccf 100644 --- a/reasoner/udf/src/test/java/com/antgroup/openspg/reasoner/udf/test/UdfTest.java +++ b/reasoner/udf/src/test/java/com/antgroup/openspg/reasoner/udf/test/UdfTest.java @@ -183,6 +183,10 @@ public void testCast() { Assert.assertEquals(rst, 60L); rst = udfMeta.invoke("60.1", "float"); Assert.assertEquals(rst, 60.1); + rst = udfMeta.invoke("3.3e7", "long"); + Assert.assertEquals(rst, 33000000L); + rst = udfMeta.invoke("1.234E2", "double"); + Assert.assertEquals(rst, 123.4); try { udfMeta.invoke("abc", "float"); Assert.assertTrue(false); From 1d3abb0e4c9c3683afc511509e60970be0cc7a51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=9F=B9=E9=BE=99?= Date: Mon, 1 Jul 2024 14:17:35 +0800 Subject: [PATCH 25/29] fix(reasoner): fix bugs for edge limit (#301) --- .../local/main/KgReasonerTopKFilmTest.java | 41 +++++++++++++++++++ .../reasoner/pattern/PatternMatcher.java | 4 +- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/KgReasonerTopKFilmTest.java b/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/KgReasonerTopKFilmTest.java index 1572df587..2ba74a0b3 100644 --- a/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/KgReasonerTopKFilmTest.java +++ b/reasoner/runner/local-runner/src/test/java/com/antgroup/openspg/reasoner/runner/local/main/KgReasonerTopKFilmTest.java @@ -416,4 +416,45 @@ private void doTest12() { Assert.assertEquals("f1", result.get(0)[1]); Assert.assertEquals("700", result.get(0)[2]); } + + @Test + public void test13() { + FileMutex.runTestWithMutex(this::doTest13); + } + + private void doTest13() { + String dsl = + "match (s:Film)-[f:starOfFilm PER_NODE_LIMIT 1]->(star:FilmStar) where s.id = 'root' return s.id, star.id"; + List result = runTestResult(dsl); + Assert.assertEquals(1, result.size()); + Assert.assertEquals(2, result.get(0).length); + Assert.assertEquals("root", result.get(0)[0]); + Assert.assertEquals("L1_1_star", result.get(0)[1]); + } + + @Test + public void test14() { + FileMutex.runTestWithMutex(this::doTest14); + } + + private void doTest14() { + String dsl = + "\n" + + "GraphStructure {\n" + + " s [Film, __start__='true']\n" + + " star [FilmStar]\n" + + " s->star [starOfFilm, PRE_NODE_LIMIT=1] as sf \n" + + "}\n" + + "Rule {\n" + + "R1: s.id=='root'\n" + + "}\n" + + "Action {\n" + + " get(s.id, star.id)\n" + + "}"; + List result = runTestResult(dsl); + Assert.assertEquals(1, result.size()); + Assert.assertEquals(2, result.get(0).length); + Assert.assertEquals("root", result.get(0)[0]); + Assert.assertEquals("L1_1_star", result.get(0)[1]); + } } diff --git a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/pattern/PatternMatcher.java b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/pattern/PatternMatcher.java index 0566f738f..06c3bdd9c 100644 --- a/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/pattern/PatternMatcher.java +++ b/reasoner/runner/runner-common/src/main/java/com/antgroup/openspg/reasoner/pattern/PatternMatcher.java @@ -256,7 +256,9 @@ public boolean test(IEdge e) { + JSON.toJSONString(dstVertexRuleList)); } } - + if (patternConnection.limit() != null && patternConnection.limit() > 0) { + limit = new Long(patternConnection.limit()); + } List> validEdges = matchEdges( vertexContext, willMatchEdgeList, patternConnection, pattern, edgeRuleMap, limit); From 87cbe1e39c43ff768e4151fd1cd599c4b02c1067 Mon Sep 17 00:00:00 2001 From: wenchengyao Date: Tue, 9 Jul 2024 10:30:53 +0800 Subject: [PATCH 26/29] fix(reasoner): fix udf json_get (#317) Author: wenchengyao Date: Tue Jul 9 10:30:53 2024 +0800 --- .../udf/builtin/udf/JsonStringGet.java | 38 +++++++++++-------- .../openspg/reasoner/udf/test/UdfTest.java | 35 ++++++++--------- 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/builtin/udf/JsonStringGet.java b/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/builtin/udf/JsonStringGet.java index 8f6ea322d..35445ca03 100644 --- a/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/builtin/udf/JsonStringGet.java +++ b/reasoner/udf/src/main/java/com/antgroup/openspg/reasoner/udf/builtin/udf/JsonStringGet.java @@ -14,9 +14,8 @@ package com.antgroup.openspg.reasoner.udf.builtin.udf; import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONPath; +import com.alibaba.fastjson.JSONValidator; import com.antgroup.openspg.reasoner.udf.model.UdfDefine; import java.util.ArrayList; import java.util.List; @@ -24,6 +23,21 @@ public class JsonStringGet { + /** + * @param jsonString + * @return + */ + private static Object parseJson(String jsonString) { + JSONValidator validator = JSONValidator.from(jsonString); + JSONValidator.Type type = validator.getType(); + if (type == JSONValidator.Type.Object) { + return JSON.parseObject(jsonString); + } else if (type == JSONValidator.Type.Array) { + return JSON.parseArray(jsonString); + } + return null; + } + /** * @param plainJson * @param jsonPath @@ -32,26 +46,18 @@ public class JsonStringGet { @UdfDefine(name = "json_get", compatibleName = "UDF_JsonGet") public Object jsonStrGet(String plainJson, String jsonPath) { try { - JSONObject jsonObject = JSON.parseObject(plainJson); + if (plainJson == null) { + return ""; + } + Object jsonObject = parseJson(plainJson); if (jsonObject != null) { Object result = JSONPath.eval(jsonObject, jsonPath); if (result != null) { return result; } } - } catch (Exception e1) { - try { - JSONArray jsonArray = JSON.parseArray(plainJson); - for (Object item : jsonArray) { - JSONObject jsonObject = (JSONObject) item; - Object result = JSONPath.eval(jsonObject, jsonPath); - if (result != null) { - return result; - } - } - } catch (Exception e2) { - return ""; - } + } catch (Exception e) { + return ""; } return ""; } diff --git a/reasoner/udf/src/test/java/com/antgroup/openspg/reasoner/udf/test/UdfTest.java b/reasoner/udf/src/test/java/com/antgroup/openspg/reasoner/udf/test/UdfTest.java index d47cafccf..82d0cb248 100644 --- a/reasoner/udf/src/test/java/com/antgroup/openspg/reasoner/udf/test/UdfTest.java +++ b/reasoner/udf/src/test/java/com/antgroup/openspg/reasoner/udf/test/UdfTest.java @@ -13,6 +13,7 @@ package com.antgroup.openspg.reasoner.udf.test; +import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.antgroup.openspg.reasoner.common.types.KTArray; import com.antgroup.openspg.reasoner.common.types.KTBoolean$; @@ -67,51 +68,45 @@ public void testReMatch() { @Test public void testJsonGet() { UdfMng mng = UdfMngFactory.getUdfMng(); - String params = "{'v':'123'}"; + String params = "{\"v\":\"123\"}"; IUdfMeta udfMeta = mng.getUdfMeta("json_get", Lists.newArrayList(KTString$.MODULE$, KTString$.MODULE$)); Object rst = udfMeta.invoke(params, "$.v"); - Assert.assertEquals(rst, "123"); - } - - @Test - public void testJsonGet1() { - UdfMng mng = UdfMngFactory.getUdfMng(); - String params = "[{'v':'123'}]"; - IUdfMeta udfMeta = - mng.getUdfMeta("json_get", Lists.newArrayList(KTString$.MODULE$, KTString$.MODULE$)); - Object rst = udfMeta.invoke(params, "$.v"); - Assert.assertEquals(rst, "123"); + Assert.assertEquals("123", rst); } @Test public void testJsonGet2() { UdfMng mng = UdfMngFactory.getUdfMng(); - String params = "[{'v':'123'}, {'k':'456'}]"; + String params = "[{\"v\":\"123\"}, {\"k\":\"456\"}]"; IUdfMeta udfMeta = mng.getUdfMeta("json_get", Lists.newArrayList(KTString$.MODULE$, KTString$.MODULE$)); Object rst = udfMeta.invoke(params, "$.k"); - Assert.assertEquals(rst, "456"); + Assert.assertEquals(JSONArray.parse("[\"456\"]"), rst); } @Test public void testJsonGet3() { UdfMng mng = UdfMngFactory.getUdfMng(); - String params = "[{'v': {'v1': '111', 'v2': '222'}}, {'k': {'k1': '333', 'k2': '444'}}]"; + String params = + "[{\"v\": {\"v1\": \"111\", \"v2\": \"222\"}}, {\"k\": {\"k1\": \"333\", \"k2\": \"444\"}}]"; IUdfMeta udfMeta = mng.getUdfMeta("json_get", Lists.newArrayList(KTString$.MODULE$, KTString$.MODULE$)); Object rst = udfMeta.invoke(params, "$.k.k2"); - Assert.assertEquals(rst, "444"); + Assert.assertEquals(JSONArray.parse("[\"444\"]"), rst); } @Test public void testJsonGet4() { UdfMng mng = UdfMngFactory.getUdfMng(); - String params = "[{'案由': '打架斗殴', '日期': '20240101'}, {'案由': '制造毒品', '日期': '20240202'}]"; + String params = + "[{\"案由\": \"打架斗殴\", \"日期\": \"20240101\"}, {\"案由\": \"制造毒品\", \"日期\": \"20240202\"}]"; IUdfMeta udfMeta = mng.getUdfMeta("json_get", Lists.newArrayList(KTString$.MODULE$, KTString$.MODULE$)); Object rst = udfMeta.invoke(params, "$[案由 rlike '(.*)毒品(.*)'].案由"); - Assert.assertEquals(rst, "制造毒品"); + Assert.assertEquals(JSONArray.parse("[\"制造毒品\"]"), rst); + rst = udfMeta.invoke(params, null); + Assert.assertEquals("", rst); } @Test @@ -126,12 +121,12 @@ public void testRdfProperty() { + "}"; ; Map paramsMap = JSONObject.parseObject(params, Map.class); - paramsMap.put("basicInfo", "{'v':'123'}"); + paramsMap.put("basicInfo", "{\"v\":\"123\"}"); IUdfMeta udfMeta = mng.getUdfMeta( "get_rdf_property", Lists.newArrayList(KTObject$.MODULE$, KTString$.MODULE$)); Object rst = udfMeta.invoke(paramsMap, "v"); - Assert.assertEquals(rst, "123"); + Assert.assertEquals("123", rst); } @Test From 25a4c2f50b0b25667b27d2b1c4bef4fb665efb2f Mon Sep 17 00:00:00 2001 From: FishJoy Date: Fri, 12 Jul 2024 16:52:48 +0800 Subject: [PATCH 27/29] fix(reasoner): fix unit test in hypertension (#321) --- .../reasoner/common/constants/Constants.java | 3 + .../thinker/engine/DefaultThinker.java | 4 +- .../reasoner/thinker/engine/GraphStore.java | 9 ++- .../reasoner/thinker/engine/InfGraph.java | 33 +++++++++-- .../logic/graph/CombinationEntity.java | 29 ++++++++++ .../reasoner/thinker/logic/graph/Element.java | 4 ++ .../reasoner/thinker/logic/graph/Entity.java | 5 ++ .../reasoner/thinker/logic/graph/Node.java | 3 + .../thinker/logic/graph/Predicate.java | 5 ++ .../reasoner/thinker/logic/graph/Triple.java | 7 +++ .../logic/rule/exact/QlExpressCondition.java | 3 +- .../logic/rule/visitor/RuleExecutor.java | 33 +++++++++-- .../openspg/reasoner/thinker/GraphUtil.java | 9 ++- .../openspg/reasoner/thinker/MedTests.java | 58 ++++++++++++++----- .../src/test/resources/Hypertension.txt | 54 ++++++++++------- .../thinker/src/test/resources/Medical.txt | 16 +++++ 16 files changed, 224 insertions(+), 51 deletions(-) diff --git a/reasoner/common/src/main/java/com/antgroup/openspg/reasoner/common/constants/Constants.java b/reasoner/common/src/main/java/com/antgroup/openspg/reasoner/common/constants/Constants.java index 0cb6f45ee..c17372b7f 100644 --- a/reasoner/common/src/main/java/com/antgroup/openspg/reasoner/common/constants/Constants.java +++ b/reasoner/common/src/main/java/com/antgroup/openspg/reasoner/common/constants/Constants.java @@ -55,6 +55,9 @@ public class Constants { public static final String SPG_REASONER_PLAN_PRETTY_PRINT_LOGGER_ENABLE = "spg.reasoner.plan.pretty.print.logger.enable"; + /** enable strict mode in thinker during rule engine execution, default is false */ + public static final String SPG_REASONER_THINKER_STRICT = "spg.reasoner.thinker.strict"; + /** start label config */ public static final String START_LABEL = "start_label"; diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/DefaultThinker.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/DefaultThinker.java index 1954164f3..ebcb4522e 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/DefaultThinker.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/DefaultThinker.java @@ -46,13 +46,13 @@ public List find(Element s, Element p, Element o) { public List find(Element s, Element p, Element o, Map context) { this.infGraph.clear(); Triple pattern = Triple.create(s, p, o); - List result = this.infGraph.find(pattern, context); + List result = this.infGraph.find(pattern, context == null ? new HashMap<>() : context); return result; } @Override public List find(Node s, Map context) { this.infGraph.clear(); - return this.infGraph.find(s, context); + return this.infGraph.find(s, context == null ? new HashMap<>() : context); } } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/GraphStore.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/GraphStore.java index a0b6a6945..8c0160aa8 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/GraphStore.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/GraphStore.java @@ -16,6 +16,7 @@ import com.antgroup.openspg.reasoner.common.constants.Constants; import com.antgroup.openspg.reasoner.common.graph.edge.Direction; import com.antgroup.openspg.reasoner.common.graph.edge.IEdge; +import com.antgroup.openspg.reasoner.common.graph.edge.SPO; import com.antgroup.openspg.reasoner.common.graph.property.IProperty; import com.antgroup.openspg.reasoner.common.graph.vertex.IVertex; import com.antgroup.openspg.reasoner.common.graph.vertex.IVertexId; @@ -85,8 +86,9 @@ protected List getTriple(Triple triple, Predicate predicate) { this.graphState.getEdges( IVertexId.from(s.getId(), s.getType()), null, null, null, Direction.OUT); for (IEdge edge : edges) { + SPO spo = new SPO(edge.getType()); if (edge.getTargetId().equals(IVertexId.from(o.getId(), o.getType())) - && edge.getType().equals(p.getName())) { + && spo.getP().equals(p.getName())) { triples.add( new Triple(triple, predicate, new Value(edge.getValue().get(predicate.getName())))); } @@ -95,12 +97,13 @@ protected List getTriple(Triple triple, Predicate predicate) { } private Triple edgeToTriple(IEdge edge) { + SPO spo = new SPO(edge.getType()); if (edge.getDirection() == Direction.OUT) { return new Triple( new Entity( (String) edge.getValue().get(Constants.EDGE_FROM_ID_KEY), (String) edge.getValue().get(Constants.EDGE_FROM_ID_TYPE_KEY)), - new Predicate(edge.getType()), + new Predicate(spo.getP()), new Entity( (String) edge.getValue().get(Constants.EDGE_TO_ID_KEY), (String) edge.getValue().get(Constants.EDGE_TO_ID_TYPE_KEY))); @@ -109,7 +112,7 @@ private Triple edgeToTriple(IEdge edge) { new Entity( (String) edge.getValue().get(Constants.EDGE_FROM_ID_KEY), (String) edge.getValue().get(Constants.EDGE_FROM_ID_TYPE_KEY)), - new Predicate(edge.getType()), + new Predicate(spo.getP()), new Entity( (String) edge.getValue().get(Constants.EDGE_TO_ID_KEY), (String) edge.getValue().get(Constants.EDGE_TO_ID_TYPE_KEY))); diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/InfGraph.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/InfGraph.java index e67a40ec6..71a1d2582 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/InfGraph.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/engine/InfGraph.java @@ -13,6 +13,7 @@ package com.antgroup.openspg.reasoner.thinker.engine; +import com.antgroup.openspg.reasoner.common.constants.Constants; import com.antgroup.openspg.reasoner.thinker.TripleStore; import com.antgroup.openspg.reasoner.thinker.logic.LogicNetwork; import com.antgroup.openspg.reasoner.thinker.logic.Result; @@ -93,6 +94,8 @@ private void prepareContext(Map context) { private List inference(Element pattern, Map context) { List rst = new LinkedList<>(); + boolean strictMode = + (Boolean) context.getOrDefault(Constants.SPG_REASONER_THINKER_STRICT, false); for (Rule rule : logicNetwork.getBackwardRules(pattern)) { List body = rule.getBody().stream().map(ClauseEntry::toElement).collect(Collectors.toList()); @@ -100,7 +103,8 @@ private List inference(Element pattern, Map context) { if (CollectionUtils.isEmpty(data)) { TreeLogger traceLogger = new TreeLogger(rule.getRoot().toString()); Boolean ret = - rule.getRoot().accept(new LinkedList<>(), context, new RuleExecutor(), traceLogger); + rule.getRoot() + .accept(new LinkedList<>(), context, new RuleExecutor(strictMode), traceLogger); traceLogger.setCurrentNodeMsg(rule.getDesc()); if (ret) { Element ele = rule.getHead().toElement(); @@ -115,7 +119,8 @@ private List inference(Element pattern, Map context) { for (List d : data) { TreeLogger traceLogger = new TreeLogger(rule.getRoot().toString()); List dList = d.stream().map(Result::getData).collect(Collectors.toList()); - Boolean ret = rule.getRoot().accept(dList, context, new RuleExecutor(), traceLogger); + Boolean ret = + rule.getRoot().accept(dList, context, new RuleExecutor(strictMode), traceLogger); List msg = d.stream() .map(Result::getTraceLog) @@ -193,6 +198,22 @@ private List> prepareElements( evidence.addAll(prepareElement(e, context)); } } + } else if (!e.canInstantiated()) { + List> singeRst = prepareElement(null, (Triple) e, context); + if (CollectionUtils.isEmpty(elements)) { + elements.addAll(singeRst); + } else { + List> tmpElements = new LinkedList<>(); + for (List ele : elements) { + for (List single : singeRst) { + List cp = new LinkedList<>(ele); + cp.addAll(single); + tmpElements.add(cp); + } + } + elements.clear(); + elements = tmpElements; + } } else { Triple t = (Triple) e; if (t.getSubject().alias().equals(s.alias())) { @@ -213,9 +234,11 @@ private List> prepareElements( choose.add(e); if (CollectionUtils.isEmpty(elements)) { Triple triple = buildTriple(null, s, t); - List> singeRst = prepareElement(null, triple, context); - if (CollectionUtils.isNotEmpty(singeRst)) { - elements.addAll(singeRst); + if (triple != null) { + List> singeRst = prepareElement(null, triple, context); + if (CollectionUtils.isNotEmpty(singeRst)) { + elements.addAll(singeRst); + } } } else { List> tmpElements = new LinkedList<>(); diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/CombinationEntity.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/CombinationEntity.java index 08a7d560c..1fb24d48e 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/CombinationEntity.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/CombinationEntity.java @@ -16,6 +16,8 @@ import com.alibaba.fastjson.JSON; import java.util.List; import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; public class CombinationEntity extends Element { private List entityList; @@ -32,6 +34,33 @@ public CombinationEntity(List entityList, String alias) { this.alias = alias; } + @Override + public boolean canInstantiated() { + return false; + } + + public Set types() { + return entityList.stream().map(Entity::getType).collect(Collectors.toSet()); + } + + public boolean matches(Element other) { + if (other == null) { + return false; + } + if (other instanceof Node) { + return types().contains(((Node) other).getType()); + } + if (other instanceof Entity) { + return entityList.contains(other); + } + return equals(other); + } + + @Override + public String alias() { + return this.alias; + } + /** * Getter method for property entityList. * diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Element.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Element.java index d5cd0024d..1aff3fbd2 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Element.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Element.java @@ -29,6 +29,10 @@ public Element bind(Element pattern) { return this; } + public boolean canInstantiated() { + return true; + } + public String alias() { throw new UnsupportedOperationException( this.getClass().getSimpleName() + " cannot support", null); diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Entity.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Entity.java index d17461f1c..05212ad53 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Entity.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Entity.java @@ -121,6 +121,11 @@ public String alias() { return this.alias; } + @Override + public boolean canInstantiated() { + return false; + } + @Override public Element cleanAlias() { return new Entity(this.id, this.type); diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Node.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Node.java index 9807fcfe3..9d75fce30 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Node.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Node.java @@ -68,6 +68,9 @@ public boolean matches(Element other) { public Element bind(Element pattern) { if (pattern instanceof Entity) { return new Entity(((Entity) pattern).getId(), this.type, ((Entity) pattern).getAlias()); + } else if (pattern instanceof CombinationEntity) { + Entity entity = ((CombinationEntity) pattern).getEntityList().get(0); + return new Entity(entity.getId(), entity.getType(), entity.getAlias()); } else { return this; } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Predicate.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Predicate.java index c4edc5373..7129b26e4 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Predicate.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Predicate.java @@ -96,6 +96,11 @@ public boolean matches(Element other) { return equals(other); } + @Override + public boolean canInstantiated() { + return false; + } + public String alias() { return alias; } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Triple.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Triple.java index 61b0e62b5..b0e1f27d9 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Triple.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/graph/Triple.java @@ -53,6 +53,13 @@ public Element bind(Element pattern) { } } + @Override + public boolean canInstantiated() { + return !(!subject.canInstantiated() + && !predicate.canInstantiated() + && !object.canInstantiated()); + } + public String alias() { return predicate.alias(); } diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/exact/QlExpressCondition.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/exact/QlExpressCondition.java index 7590b1a12..012a850a2 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/exact/QlExpressCondition.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/exact/QlExpressCondition.java @@ -98,7 +98,8 @@ public Boolean execute(List spoList, Map context, TreeL private boolean absent(Map context) throws Exception { if (qlExpress.toLowerCase().contains("get_value") - || qlExpress.toLowerCase().contains("get_spo")) { + || qlExpress.toLowerCase().contains("get_spo") + || qlExpress.toLowerCase().contains("rule_value")) { return false; } Map> vars = varsCache.get(qlExpress); diff --git a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/visitor/RuleExecutor.java b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/visitor/RuleExecutor.java index f30d2f38f..6a0633b58 100644 --- a/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/visitor/RuleExecutor.java +++ b/reasoner/thinker/src/main/java/com/antgroup/openspg/reasoner/thinker/logic/rule/visitor/RuleExecutor.java @@ -24,6 +24,15 @@ import java.util.Map; public class RuleExecutor implements RuleNodeVisitor { + private boolean strictMode = true; + + public RuleExecutor() { + this.strictMode = false; + } + + public RuleExecutor(boolean strictMode) { + this.strictMode = strictMode; + } @Override public Boolean visit( @@ -31,7 +40,11 @@ public Boolean visit( Boolean ret = null; for (Node child : node.getChildren()) { Boolean c = child.accept(spoList, context, this, logger.addChild(child.toString())); - c = c == null ? true : c; + if (strictMode) { + c = c == null ? false : c; + } else { + c = c == null ? true : c; + } if (ret == null) { ret = c; } else { @@ -49,7 +62,11 @@ public Boolean visit( Boolean ret = null; for (Node child : node.getChildren()) { Boolean c = child.accept(spoList, context, this, logger.addChild(child.toString())); - c = c == null ? true : c; + if (strictMode) { + c = c == null ? false : c; + } else { + c = c == null ? true : c; + } if (ret == null) { ret = c; } else { @@ -67,7 +84,11 @@ public Boolean visit( Boolean ret = null; Node child = node.getChild(); Boolean r = child.accept(spoList, context, this, logger); - r = r == null ? true : r; + if (strictMode) { + r = r == null ? false : r; + } else { + r = r == null ? true : r; + } ret = !r; logger.log(ret); logger.setCurrentNodeRst(ret); @@ -80,6 +101,10 @@ public Boolean visit( Boolean ret = node.execute(spoList, context, logger); logger.log(ret); logger.setCurrentNodeRst(ret); - return ret == null ? true : ret; + if (strictMode) { + return ret == null ? false : ret; + } else { + return ret == null ? true : ret; + } } } diff --git a/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/GraphUtil.java b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/GraphUtil.java index b82eeaa42..b89f6be50 100644 --- a/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/GraphUtil.java +++ b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/GraphUtil.java @@ -17,6 +17,7 @@ import com.antgroup.openspg.reasoner.common.constants.Constants; import com.antgroup.openspg.reasoner.common.graph.edge.Direction; import com.antgroup.openspg.reasoner.common.graph.edge.IEdge; +import com.antgroup.openspg.reasoner.common.graph.edge.SPO; import com.antgroup.openspg.reasoner.common.graph.edge.impl.Edge; import com.antgroup.openspg.reasoner.common.graph.property.IProperty; import com.antgroup.openspg.reasoner.common.graph.property.IVersionProperty; @@ -44,8 +45,14 @@ public static Edge makeEdge( valueMap.put(0L, entry.getValue()); props.put(entry.getKey(), valueMap); } + SPO spo = new SPO(from.getId().getType(), edgeType, to.getId().getType()); return new Edge<>( - from.getId(), to.getId(), new VertexVersionProperty(props), 0L, Direction.OUT, edgeType); + from.getId(), + to.getId(), + new VertexVersionProperty(props), + 0L, + Direction.OUT, + spo.toString()); } public static Vertex makeVertex(String id, String type, Object... kvs) { diff --git a/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/MedTests.java b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/MedTests.java index 9a5d9023b..a837b2883 100644 --- a/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/MedTests.java +++ b/reasoner/thinker/src/test/java/com/antgroup/openspg/reasoner/thinker/MedTests.java @@ -13,36 +13,35 @@ package com.antgroup.openspg.reasoner.thinker; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.TypeReference; +import com.antgroup.openspg.reasoner.common.constants.Constants; import com.antgroup.openspg.reasoner.common.graph.property.IProperty; -import com.antgroup.openspg.reasoner.common.graph.property.impl.VertexVersionProperty; import com.antgroup.openspg.reasoner.common.graph.vertex.IVertexId; import com.antgroup.openspg.reasoner.common.graph.vertex.impl.Vertex; import com.antgroup.openspg.reasoner.graphstate.GraphState; -import com.antgroup.openspg.reasoner.graphstate.impl.MemGraphState; import com.antgroup.openspg.reasoner.thinker.catalog.ResourceLogicCatalog; import com.antgroup.openspg.reasoner.thinker.engine.DefaultThinker; import com.antgroup.openspg.reasoner.thinker.logic.Result; import com.antgroup.openspg.reasoner.thinker.logic.graph.Entity; +import com.antgroup.openspg.reasoner.thinker.logic.graph.Node; import com.antgroup.openspg.reasoner.thinker.logic.graph.Predicate; import com.antgroup.openspg.reasoner.thinker.logic.graph.Value; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import org.junit.Assert; import org.junit.Test; public class MedTests { private GraphState buildGraphState() { - GraphState graphState = new MemGraphState(); - Vertex vertex1 = - new Vertex<>( - IVertexId.from("尿酸", "Med.Examination"), - new VertexVersionProperty( - "highExplain", "highExplain desc", "lowExplain", "lowExplain desc")); - graphState.addVertex(vertex1); - - return graphState; + GraphUtil.makeVertex( + "尿酸", + "Med.Examination", + "highExplain", + "highExplain desc", + "lowExplain", + "lowExplain desc"); + return GraphUtil.buildMemState(Arrays.asList(vertex1), new LinkedList<>()); } @Test @@ -87,4 +86,35 @@ public void testHigh() { context); Assert.assertTrue(triples.size() == 2); } + + @Test + public void testStrict() { + ResourceLogicCatalog logicCatalog = new ResourceLogicCatalog("/Medical.txt"); + logicCatalog.init(); + Thinker thinker = new DefaultThinker(buildGraphState(), logicCatalog); + Map context = new HashMap<>(); + context.put("value", "阳性"); + context.put(Constants.SPG_REASONER_THINKER_STRICT, true); + List triples = + thinker.find( + new Entity("尿酸", "Med.Examination"), + new Predicate("abnormalRule"), + new Value(), + context); + Assert.assertTrue(triples.size() == 0); + } + + @Test + public void testHepatitis() { + ResourceLogicCatalog logicCatalog = new ResourceLogicCatalog("/Medical.txt"); + logicCatalog.init(); + Thinker thinker = new DefaultThinker(buildGraphState(), logicCatalog); + String str = + "{\"spg.reasoner.thinker.strict\":true,\"乙肝表面抗原\":\"225.000\",\"乙肝表面抗原_lower\":\"0\",\"乙肝表面抗原_upper\":\"0.5\",\"乙肝表面抗体\":\"0.1\",\"乙肝表面抗体_lower\":\"0\",\"乙肝表面抗体_upper\":\"10\",\"乙肝e抗原\":\"69.000\",\"乙肝e抗原_lower\":\"0\",\"乙肝e抗原_upper\":\"0.5\",\"乙肝e抗体\":\"0.00\",\"乙肝e抗体_lower\":\"0\",\"乙肝e抗体_upper\":\"0.2\",\"乙肝核心抗体\":\"4.050\",\"乙肝核心抗体_lower\":\"0\",\"乙肝核心抗体_upper\":\"0.9\"}"; + Map context = + JSONObject.parseObject(str, new TypeReference>() {}); + List triples = + thinker.find(null, new Predicate("确诊"), new Node("Medical.DiseaseTerm"), context); + Assert.assertTrue(triples.size() == 1); + } } diff --git a/reasoner/thinker/src/test/resources/Hypertension.txt b/reasoner/thinker/src/test/resources/Hypertension.txt index 0dd1fb5a1..9da6e8297 100644 --- a/reasoner/thinker/src/test/resources/Hypertension.txt +++ b/reasoner/thinker/src/test/resources/Hypertension.txt @@ -1,63 +1,75 @@ Define (血压水平分级/`1级高血压`) { - R1: 收缩压>=140 or 舒张压>=90 + 收缩压>=140 or 舒张压>=90 } Define (血压水平分级/`2级高血压`) { - R1: 收缩压>=160 or 舒张压>=100 + 收缩压>=160 or 舒张压>=100 } Define (血压水平分级/`血压正常高值`) { - R1: 收缩压>=120 or 舒张压>=80 + 收缩压>=120 or 舒张压>=80 } Define (疾病/`高血压`) { - R1: 血压水平分级/`1级高血压` or 血压水平分级/`2级高血压` + 血压水平分级/`1级高血压` or 血压水平分级/`2级高血压` } Define (疾病/`肥胖`) { - R1: BMI>=30 + BMI>=30 } Define (疾病/`慢性肾病3期`) { - R1: GFR>=30 + GFR>=30 } Define (高血压分层/`心血管危险因素-肥胖`) { - R1: 疾病/`肥胖` - R2: 疾病/`腹型肥胖` + 疾病/`肥胖` +} + +Define (高血压分层/`心血管危险因素-肥胖`) { + 疾病/`腹型肥胖` } Define (高血压分层/`靶器官损害-肾脏`) { - R1: 疾病/`慢性肾病3期` + 疾病/`慢性肾病3期` +} + +Define (高血压分层/`临床并发症-心脏疾病`) { + 疾病/`冠心病` } Define (高血压分层/`临床并发症-心脏疾病`) { - R1: 疾病/`冠心病` - R2: 疾病/`心力衰竭` - R3: 疾病/`心房颤动` + 疾病/`心力衰竭` +} + +Define (高血压分层/`临床并发症-心脏疾病`) { + 疾病/`心房颤动` } Define (高血压分层/`心血管危险因素`) { - R1: 高血压分级/`心血管危险因素-肥胖` + 高血压分级/`心血管危险因素-肥胖` } Define (高血压分层/`靶器官损害`) { - R1: 高血压分级/`靶器官损害-肾脏` + 高血压分级/`靶器官损害-肾脏` } Define (高血压分层/`临床并发症`) { - R1: 高血压分级/`临床并发症-心脏疾病` + 高血压分级/`临床并发症-心脏疾病` +} + +Define (药品/`多药方案`) { + 收缩压>=160 and 舒张压>=100 } Define (药品/`多药方案`) { - R1: 收缩压>=160 and 舒张压>=100 - R2: 收缩压-目标收缩压上界>=20 + 收缩压-目标收缩压上界>=20 } -Define ()-[:基本用药方案]->(:药品/`ACEI+噻嗪类利尿剂`) { - R1: 疾病/`高血压` and 药品/`多药方案` +Define ()-[:基本用药方案]->(:药品/`ACEI` + 药品/`噻嗪类利尿剂`) { + 疾病/`高血压` and 药品/`多药方案` } -Define ()-[:基本用药方案]->(:药品/`ARB+噻嗪类利尿剂`) { - R1: 疾病/`高血压` and 药品/`多药方案` +Define ()-[:基本用药方案]->(:药品/`ARB` + 药品/`噻嗪类利尿剂`) { + 疾病/`高血压` and 药品/`多药方案` } \ No newline at end of file diff --git a/reasoner/thinker/src/test/resources/Medical.txt b/reasoner/thinker/src/test/resources/Medical.txt index 28bc9606e..98db803ed 100644 --- a/reasoner/thinker/src/test/resources/Medical.txt +++ b/reasoner/thinker/src/test/resources/Medical.txt @@ -24,4 +24,20 @@ Define (a:Med.Examination)-[:abnormalRule]->(c: string) { Define (a:Med.Examination)-[:abnormalRule]->(c: string) { R1: (a)-[:abnormalValue]->(b: Med.ExaminationResult/`偏低`) AND (a)-[: lowExplain]->(c) +} + +Define()-[: 确诊]->(: Medical.DiseaseTerm/`乙型肝炎大三阳`) { + r: (: Medical.ExaminationTerm/`乙肝表面抗原`)-[: abnormalValue]->(: Medical.AbnormalExaminationIndicator/`阳性`) and (: Medical.ExaminationTerm/`乙肝e抗原`)-[: abnormalValue]->(: Medical.AbnormalExaminationIndicator/`阳性`) and (: Medical.ExaminationTerm/`乙肝核心抗体`)-[: abnormalValue]->(: Medical.AbnormalExaminationIndicator/`阳性`) +} + +Define (a:Medical.ExaminationTerm/`乙肝表面抗原`)-[:abnormalValue]->(c: Medical.AbnormalExaminationIndicator/`阳性`) { + R1: (乙肝表面抗原>rule_value(乙肝表面抗原_upper != null, 乙肝表面抗原_upper, 1)) +} + +Define (a:Medical.ExaminationTerm/`乙肝e抗原`)-[:abnormalValue]->(c: Medical.AbnormalExaminationIndicator/`阳性`) { + R1: (乙肝e抗原>rule_value(乙肝e抗原_upper != null, 乙肝e抗原_upper, 2.1)) +} + +Define (a:Medical.ExaminationTerm/`乙肝核心抗体`)-[:abnormalValue]->(c: Medical.AbnormalExaminationIndicator/`阳性`) { + R1: (乙肝核心抗体>rule_value(乙肝核心抗体_upper != null, 乙肝核心抗体_upper, 2.1)) } \ No newline at end of file From 296ea898761886a699c1f52e8a0ce9e487a82d72 Mon Sep 17 00:00:00 2001 From: matthewhyx Date: Mon, 5 Aug 2024 13:04:29 +0800 Subject: [PATCH 28/29] resolve conflict --- .../com/antgroup/openspg/reasoner/lube/catalog/Catalog.scala | 3 --- 1 file changed, 3 deletions(-) diff --git a/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/catalog/Catalog.scala b/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/catalog/Catalog.scala index 852c949dc..70748752c 100644 --- a/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/catalog/Catalog.scala +++ b/reasoner/lube-api/src/main/scala/com/antgroup/openspg/reasoner/lube/catalog/Catalog.scala @@ -82,9 +82,6 @@ abstract class Catalog() extends Serializable { */ def getConnection(typeName: String): Set[AbstractConnection] = { val finalType = LabelTypeUtils.getMetaType(typeName) - if (!connections.contains(finalType)) { - throw ConnectionNotFoundException(s"$finalType not found.", null) - } connections.getOrElse(finalType, mutable.Set.empty).toSet } From 8f6e434b24a0f4bdf019e2292b23acd329a47dfe Mon Sep 17 00:00:00 2001 From: FishJoy Date: Thu, 8 Aug 2024 10:23:44 +0800 Subject: [PATCH 29/29] format --- .../reason/impl/CausalConceptReasoner.java | 9 +- .../schema/supplychain_schema_helper.py | 92 +++++++------------ .../knext/schema/marklang/concept_rule_ml.py | 87 +++++++++++------- .../api/facade/client/ConceptFacade.java | 7 +- .../dto/schema/request/SPGTypeRequest.java | 3 +- .../api/http/client/HttpConceptFacade.java | 3 +- .../forest/client/ConceptForestClient.java | 3 +- .../server/openapi/ConceptController.java | 37 ++++---- .../server/biz/schema/ConceptManager.java | 4 +- .../biz/schema/impl/ConceptManagerImpl.java | 19 +++- .../model/semantic/SPGOntologyEnum.java | 4 +- .../request/DefineTripleSemanticRequest.java | 4 +- .../request/RemoveTripleSemanticRequest.java | 4 +- .../core/schema/model/type/Concept.java | 2 +- .../concept/ConceptSemanticService.java | 1 - .../convertor/ConceptSemanticConvertor.java | 2 +- .../impl/ConceptSemanticServiceImpl.java | 5 +- .../semantic/model/TripleSemanticQuery.java | 7 +- .../repository/SemanticRepository.java | 4 +- .../schema/SemanticRepositoryImpl.java | 4 +- 20 files changed, 145 insertions(+), 156 deletions(-) diff --git a/builder/core/src/main/java/com/antgroup/openspg/builder/core/reason/impl/CausalConceptReasoner.java b/builder/core/src/main/java/com/antgroup/openspg/builder/core/reason/impl/CausalConceptReasoner.java index b8cd6bf19..18ce19a50 100644 --- a/builder/core/src/main/java/com/antgroup/openspg/builder/core/reason/impl/CausalConceptReasoner.java +++ b/builder/core/src/main/java/com/antgroup/openspg/builder/core/reason/impl/CausalConceptReasoner.java @@ -21,8 +21,8 @@ import com.antgroup.openspg.builder.model.record.property.SPGPropertyRecord; import com.antgroup.openspg.builder.model.record.property.SPGPropertyValue; import com.antgroup.openspg.core.schema.model.semantic.DynamicTaxonomySemantic; -import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.core.schema.model.semantic.SystemPredicateEnum; +import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.core.schema.model.type.ConceptList; import com.antgroup.openspg.reasoner.common.graph.vertex.IVertexId; import com.antgroup.openspg.reasoner.graphstate.GraphState; @@ -45,17 +45,14 @@ public class CausalConceptReasoner implements ConceptReasoner { @Setter private GraphState graphState; @Override - public List reason( - List records, TripleSemantic conceptSemantic) { + public List reason(List records, TripleSemantic conceptSemantic) { List results = new ArrayList<>(records); propagate(records, conceptSemantic, results); return results; } private void propagate( - List spgRecords, - TripleSemantic conceptSemantic, - List results) { + List spgRecords, TripleSemantic conceptSemantic, List results) { List toPropagated = new ArrayList<>(); for (BaseSPGRecord spgRecord : spgRecords) { if (!(spgRecord instanceof BaseAdvancedRecord)) { diff --git a/python/knext/knext/examples/supplychain/schema/supplychain_schema_helper.py b/python/knext/knext/examples/supplychain/schema/supplychain_schema_helper.py index 60a6a3a7a..cea460d27 100644 --- a/python/knext/knext/examples/supplychain/schema/supplychain_schema_helper.py +++ b/python/knext/knext/examples/supplychain/schema/supplychain_schema_helper.py @@ -15,17 +15,19 @@ # PLEASE DO NOT MODIFY THIS FILE!!! # -from knext.schema.model.schema_helper import SPGTypeHelper, PropertyHelper, RelationHelper +from knext.schema.model.schema_helper import ( + SPGTypeHelper, + PropertyHelper, + RelationHelper, +) class SupplyChain: - class Company(SPGTypeHelper): - class fundTrans(RelationHelper): transDate = PropertyHelper("transDate") transAmt = PropertyHelper("transAmt") - + cashflowDiff1Month = PropertyHelper("cashflowDiff1Month") fundTrans1MonthIn = PropertyHelper("fundTrans1MonthIn") totalTransInAmt = PropertyHelper("totalTransInAmt") @@ -40,16 +42,15 @@ class fundTrans(RelationHelper): cashflowDiff3Month = PropertyHelper("cashflowDiff3Month") fundTrans3MonthIn = PropertyHelper("fundTrans3MonthIn") fundTrans6MonthIn = PropertyHelper("fundTrans6MonthIn") - + mainSupply = RelationHelper("mainSupply") belongToIndustry = RelationHelper("belongToIndustry") sameLegalRepresentative = RelationHelper("sameLegalRepresentative") - + fundTrans = fundTrans("fundTrans") - + class CompanyEvent(SPGTypeHelper): - - + leadTo = PropertyHelper("leadTo") subject = PropertyHelper("subject") id = PropertyHelper("id") @@ -59,58 +60,43 @@ class CompanyEvent(SPGTypeHelper): trend = PropertyHelper("trend") index = PropertyHelper("index") eventTime = PropertyHelper("eventTime") - - - + class Index(SPGTypeHelper): - - + alias = PropertyHelper("alias") id = PropertyHelper("id") description = PropertyHelper("description") name = PropertyHelper("name") stdId = PropertyHelper("stdId") - - - + class Industry(SPGTypeHelper): - - + alias = PropertyHelper("alias") id = PropertyHelper("id") description = PropertyHelper("description") name = PropertyHelper("name") stdId = PropertyHelper("stdId") - - - + class Person(SPGTypeHelper): - - + certNo = PropertyHelper("certNo") age = PropertyHelper("age") legalRepresentative = PropertyHelper("legalRepresentative") id = PropertyHelper("id") description = PropertyHelper("description") name = PropertyHelper("name") - - - + class Product(SPGTypeHelper): - - + id = PropertyHelper("id") description = PropertyHelper("description") name = PropertyHelper("name") belongTo = PropertyHelper("belongTo") belongToIndustry = PropertyHelper("belongToIndustry") hasSupplyChain = PropertyHelper("hasSupplyChain") - - - + class ProductChainEvent(SPGTypeHelper): - - + subject = PropertyHelper("subject") id = PropertyHelper("id") description = PropertyHelper("description") @@ -119,55 +105,43 @@ class ProductChainEvent(SPGTypeHelper): trend = PropertyHelper("trend") index = PropertyHelper("index") eventTime = PropertyHelper("eventTime") - + leadTo = RelationHelper("leadTo") - - + class TaxOfCompanyEvent(SPGTypeHelper): - - + alias = PropertyHelper("alias") id = PropertyHelper("id") description = PropertyHelper("description") name = PropertyHelper("name") stdId = PropertyHelper("stdId") - - - + class TaxOfProdEvent(SPGTypeHelper): - - + alias = PropertyHelper("alias") id = PropertyHelper("id") description = PropertyHelper("description") name = PropertyHelper("name") stdId = PropertyHelper("stdId") - + leadTo = RelationHelper("leadTo") - - + class TaxOfProduct(SPGTypeHelper): - - + alias = PropertyHelper("alias") id = PropertyHelper("id") description = PropertyHelper("description") name = PropertyHelper("name") stdId = PropertyHelper("stdId") - - - + class Trend(SPGTypeHelper): - - + alias = PropertyHelper("alias") id = PropertyHelper("id") description = PropertyHelper("description") name = PropertyHelper("name") stdId = PropertyHelper("stdId") - - - + Company = Company("SupplyChain.Company") CompanyEvent = CompanyEvent("SupplyChain.CompanyEvent") Index = Index("SupplyChain.Index") @@ -179,5 +153,5 @@ class Trend(SPGTypeHelper): TaxOfProdEvent = TaxOfProdEvent("SupplyChain.TaxOfProdEvent") TaxOfProduct = TaxOfProduct("SupplyChain.TaxOfProduct") Trend = Trend("SupplyChain.Trend") - - pass \ No newline at end of file + + pass diff --git a/python/knext/knext/schema/marklang/concept_rule_ml.py b/python/knext/knext/schema/marklang/concept_rule_ml.py index bca26bf64..45868e4dd 100644 --- a/python/knext/knext/schema/marklang/concept_rule_ml.py +++ b/python/knext/knext/schema/marklang/concept_rule_ml.py @@ -87,7 +87,10 @@ def parse_concept(self, expression): "please define namespace first" ) - self.dst_concept = (reasoning_concept_match.group(1), reasoning_concept_match.group(2)) + self.dst_concept = ( + reasoning_concept_match.group(1), + reasoning_concept_match.group(2), + ) self.is_reasoning = True return @@ -101,7 +104,10 @@ def parse_concept(self, expression): ) self.predicate = reasoning_po_match.group(1) - self.dst_concept = (reasoning_po_match.group(2), reasoning_po_match.group(3)) + self.dst_concept = ( + reasoning_po_match.group(2), + reasoning_po_match.group(3), + ) self.is_reasoning = True return @@ -114,9 +120,15 @@ def parse_concept(self, expression): "please define namespace first" ) - self.src_concept = (reasoning_spo_match.group(1), reasoning_spo_match.group(2)) + self.src_concept = ( + reasoning_spo_match.group(1), + reasoning_spo_match.group(2), + ) self.predicate = reasoning_spo_match.group(3) - self.dst_concept = (reasoning_spo_match.group(4), reasoning_spo_match.group(5)) + self.dst_concept = ( + reasoning_spo_match.group(4), + reasoning_spo_match.group(5), + ) self.is_reasoning = True return @@ -149,7 +161,7 @@ def parse_rule(self, rule): if len(strip_rule) > 2: if strip_rule.endswith("]]"): self.rule_quote_open = False - self.rule_text = strip_rule[2: len(strip_rule) - 2].lstrip() + self.rule_text = strip_rule[2 : len(strip_rule) - 2].lstrip() else: self.rule_text = strip_rule[2].lstrip() else: @@ -169,8 +181,12 @@ def complete_rule(self, rule): subject_name = None if self.is_reasoning: predicate_name = self.predicate - subject_type = self.src_concept[0] if len(self.src_concept) > 0 else None - subject_name = self.src_concept[1] if len(self.src_concept) > 0 else None + subject_type = ( + self.src_concept[0] if len(self.src_concept) > 0 else None + ) + subject_name = ( + self.src_concept[1] if len(self.src_concept) > 0 else None + ) object_type = self.dst_concept[0] if len(self.dst_concept) > 0 else None object_name = self.dst_concept[1] if len(self.dst_concept) > 0 else None elif self.dst_concept[0] is not None: @@ -199,26 +215,23 @@ def complete_rule(self, rule): break if self.is_reasoning: - if subject_type is None and self.predicate is None and not self.is_priority: - head = ( - f"Define ({object_type}/`{object_name}`)" - + " {\n" - ) + if ( + subject_type is None + and self.predicate is None + and not self.is_priority + ): + head = f"Define ({object_type}/`{object_name}`)" + " {\n" elif subject_type is None and self.predicate is not None: head = ( - f"Define ()-[:{predicate_name}]->(:{object_type}/`{object_name}`)" - + " {\n" + f"Define ()-[:{predicate_name}]->(:{object_type}/`{object_name}`)" + + " {\n" ) elif self.is_priority: - head = ( - f"DefinePriority ({object_type})" - + " {\n" - ) + head = f"DefinePriority ({object_type})" + " {\n" else: head = ( - f"Define (:{object_type}/`{object_name}`)-[:{predicate_name}]->" - f"(:{object_type}/`{object_name}`)" - + " {\n" + f"Define (:{object_type}/`{object_name}`)-[:{predicate_name}]->" + f"(:{object_type}/`{object_name}`)" + " {\n" ) elif subject_name is None: head = ( @@ -303,10 +316,15 @@ def submit_rule(self): if not is_blank(self.rule_text): self.concept_client.concept_define_logical_causation_post( define_logical_causation_request=rest.DefineLogicalCausationRequest( - subject_concept_type_name="Thing" if len( - self.src_concept) == 0 else f"{self.namespace}.{self.src_concept[0]}", - subject_concept_name="1" if len(self.src_concept) == 0 else self.src_concept[1], - predicate_name="conclude" if self.predicate is None else self.predicate, + subject_concept_type_name="Thing" + if len(self.src_concept) == 0 + else f"{self.namespace}.{self.src_concept[0]}", + subject_concept_name="1" + if len(self.src_concept) == 0 + else self.src_concept[1], + predicate_name="conclude" + if self.predicate is None + else self.predicate, object_concept_type_name=f"{self.namespace}.{self.dst_concept[0]}", object_concept_name=self.dst_concept[1], semantic_type="REASONING_CONCEPT", @@ -319,13 +337,18 @@ def submit_rule(self): else: self.concept_client.concept_remove_logical_causation_post( remove_logical_causation_request=rest.RemoveLogicalCausationRequest( - subject_concept_type_name="Thing" if len( - self.src_concept) == 0 else f"{self.namespace}.{self.src_concept[0]}", - subject_concept_name="1" if len(self.src_concept) == 0 else self.src_concept[1], - predicate_name="conclude" if self.predicate is None else self.predicate, + subject_concept_type_name="Thing" + if len(self.src_concept) == 0 + else f"{self.namespace}.{self.src_concept[0]}", + subject_concept_name="1" + if len(self.src_concept) == 0 + else self.src_concept[1], + predicate_name="conclude" + if self.predicate is None + else self.predicate, object_concept_type_name=f"{self.namespace}.{self.dst_concept[0]}", object_concept_name=self.dst_concept[1], - semantic_type="REASONING_CONCEPT" + semantic_type="REASONING_CONCEPT", ) ) print( @@ -349,7 +372,7 @@ def submit_rule(self): self.concept_client.concept_remove_dynamic_taxonomy_post( remove_dynamic_taxonomy_request=rest.RemoveDynamicTaxonomyRequest( object_concept_type_name=f"{self.namespace}.{self.src_concept[0]}", - object_concept_name=self.src_concept[1] + object_concept_name=self.src_concept[1], ) ) print( @@ -380,7 +403,7 @@ def submit_rule(self): subject_concept_name=self.src_concept[1], predicate_name="leadTo", object_concept_type_name=f"{self.namespace}.{self.dst_concept[0]}", - object_concept_name=self.dst_concept[1] + object_concept_name=self.dst_concept[1], ) ) print( diff --git a/server/api/facade/src/main/java/com/antgroup/openspg/server/api/facade/client/ConceptFacade.java b/server/api/facade/src/main/java/com/antgroup/openspg/server/api/facade/client/ConceptFacade.java index 8d0c152cf..21763016b 100644 --- a/server/api/facade/src/main/java/com/antgroup/openspg/server/api/facade/client/ConceptFacade.java +++ b/server/api/facade/src/main/java/com/antgroup/openspg/server/api/facade/client/ConceptFacade.java @@ -13,8 +13,6 @@ package com.antgroup.openspg.server.api.facade.client; -import java.util.List; - import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.core.schema.model.semantic.request.DefineDynamicTaxonomyRequest; import com.antgroup.openspg.core.schema.model.semantic.request.DefineTripleSemanticRequest; @@ -24,6 +22,7 @@ import com.antgroup.openspg.server.api.facade.ApiResponse; import com.antgroup.openspg.server.api.facade.dto.schema.request.ConceptRequest; import com.antgroup.openspg.server.api.facade.dto.schema.request.SPGTypeRequest; +import java.util.List; /** * The interface to query concepts that defined under a concept type, also provides method to define @@ -43,7 +42,9 @@ public interface ConceptFacade { ApiResponse queryConcept(ConceptRequest request); /** - * Query reasoning concepts detail by the concept types, the interface returned triple semantic object for rule detail. + * Query reasoning concepts detail by the concept types, the interface returned triple semantic + * object for rule detail. + * * @param request * @return */ diff --git a/server/api/facade/src/main/java/com/antgroup/openspg/server/api/facade/dto/schema/request/SPGTypeRequest.java b/server/api/facade/src/main/java/com/antgroup/openspg/server/api/facade/dto/schema/request/SPGTypeRequest.java index 3f02d6b90..4069c26b8 100644 --- a/server/api/facade/src/main/java/com/antgroup/openspg/server/api/facade/dto/schema/request/SPGTypeRequest.java +++ b/server/api/facade/src/main/java/com/antgroup/openspg/server/api/facade/dto/schema/request/SPGTypeRequest.java @@ -13,10 +13,9 @@ package com.antgroup.openspg.server.api.facade.dto.schema.request; -import java.util.List; - import com.antgroup.openspg.server.common.model.base.BaseRequest; import com.google.common.collect.Lists; +import java.util.List; import org.apache.commons.collections4.CollectionUtils; /** Query schema type. */ diff --git a/server/api/http-client/src/main/java/com/antgroup/openspg/server/api/http/client/HttpConceptFacade.java b/server/api/http-client/src/main/java/com/antgroup/openspg/server/api/http/client/HttpConceptFacade.java index e4dfddd6a..4b9d459f2 100644 --- a/server/api/http-client/src/main/java/com/antgroup/openspg/server/api/http/client/HttpConceptFacade.java +++ b/server/api/http-client/src/main/java/com/antgroup/openspg/server/api/http/client/HttpConceptFacade.java @@ -13,8 +13,6 @@ package com.antgroup.openspg.server.api.http.client; -import java.util.List; - import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.core.schema.model.semantic.request.DefineDynamicTaxonomyRequest; import com.antgroup.openspg.core.schema.model.semantic.request.DefineTripleSemanticRequest; @@ -27,6 +25,7 @@ import com.antgroup.openspg.server.api.facade.dto.schema.request.SPGTypeRequest; import com.antgroup.openspg.server.api.http.client.forest.ForestUtils; import com.antgroup.openspg.server.api.http.client.forest.client.ConceptForestClient; +import java.util.List; public class HttpConceptFacade implements ConceptFacade { diff --git a/server/api/http-client/src/main/java/com/antgroup/openspg/server/api/http/client/forest/client/ConceptForestClient.java b/server/api/http-client/src/main/java/com/antgroup/openspg/server/api/http/client/forest/client/ConceptForestClient.java index 0283ed81a..aaf54a3f3 100644 --- a/server/api/http-client/src/main/java/com/antgroup/openspg/server/api/http/client/forest/client/ConceptForestClient.java +++ b/server/api/http-client/src/main/java/com/antgroup/openspg/server/api/http/client/forest/client/ConceptForestClient.java @@ -13,8 +13,6 @@ package com.antgroup.openspg.server.api.http.client.forest.client; -import java.util.List; - import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.core.schema.model.semantic.request.DefineDynamicTaxonomyRequest; import com.antgroup.openspg.core.schema.model.semantic.request.DefineTripleSemanticRequest; @@ -31,6 +29,7 @@ import com.dtflys.forest.annotation.Post; import com.dtflys.forest.annotation.Query; import com.dtflys.forest.http.ForestResponse; +import java.util.List; @BodyType(type = "json") @Address( diff --git a/server/api/http-server/src/main/java/com/antgroup/openspg/server/api/http/server/openapi/ConceptController.java b/server/api/http-server/src/main/java/com/antgroup/openspg/server/api/http/server/openapi/ConceptController.java index 153d5fa16..75637818e 100644 --- a/server/api/http-server/src/main/java/com/antgroup/openspg/server/api/http/server/openapi/ConceptController.java +++ b/server/api/http-server/src/main/java/com/antgroup/openspg/server/api/http/server/openapi/ConceptController.java @@ -13,8 +13,6 @@ package com.antgroup.openspg.server.api.http.server.openapi; -import java.util.List; - import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.core.schema.model.semantic.request.DefineDynamicTaxonomyRequest; import com.antgroup.openspg.core.schema.model.semantic.request.DefineTripleSemanticRequest; @@ -28,6 +26,7 @@ import com.antgroup.openspg.server.api.http.server.HttpBizTemplate; import com.antgroup.openspg.server.biz.common.util.AssertUtils; import com.antgroup.openspg.server.biz.schema.ConceptManager; +import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; @@ -61,23 +60,23 @@ public ConceptList action() { }); } - @RequestMapping(value = "/getReasoningConcept", method = RequestMethod.GET) - @ResponseBody - public ResponseEntity getReasoningConcept(SPGTypeRequest request) { - return HttpBizTemplate.execute( - new HttpBizCallback>() { - @Override - public void check() { - AssertUtils.assertParamObjectIsNotNull("request", request); - AssertUtils.assertParamObjectIsNotNull("conceptTypeName", request.getNameList()); - } - - @Override - public List action() { - return conceptManager.getReasoningConceptsDetail(request.getNameList()); - } - }); - } + @RequestMapping(value = "/getReasoningConcept", method = RequestMethod.GET) + @ResponseBody + public ResponseEntity getReasoningConcept(SPGTypeRequest request) { + return HttpBizTemplate.execute( + new HttpBizCallback>() { + @Override + public void check() { + AssertUtils.assertParamObjectIsNotNull("request", request); + AssertUtils.assertParamObjectIsNotNull("conceptTypeName", request.getNameList()); + } + + @Override + public List action() { + return conceptManager.getReasoningConceptsDetail(request.getNameList()); + } + }); + } @RequestMapping(value = "/defineDynamicTaxonomy", method = RequestMethod.POST) @ResponseBody diff --git a/server/biz/schema/src/main/java/com/antgroup/openspg/server/biz/schema/ConceptManager.java b/server/biz/schema/src/main/java/com/antgroup/openspg/server/biz/schema/ConceptManager.java index 2cdb1a134..8841831b8 100644 --- a/server/biz/schema/src/main/java/com/antgroup/openspg/server/biz/schema/ConceptManager.java +++ b/server/biz/schema/src/main/java/com/antgroup/openspg/server/biz/schema/ConceptManager.java @@ -13,14 +13,13 @@ package com.antgroup.openspg.server.biz.schema; -import java.util.List; - import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.core.schema.model.semantic.request.DefineDynamicTaxonomyRequest; import com.antgroup.openspg.core.schema.model.semantic.request.DefineTripleSemanticRequest; import com.antgroup.openspg.core.schema.model.semantic.request.RemoveDynamicTaxonomyRequest; import com.antgroup.openspg.core.schema.model.semantic.request.RemoveTripleSemanticRequest; import com.antgroup.openspg.core.schema.model.type.ConceptList; +import java.util.List; /** Provide method to manage concept */ public interface ConceptManager { @@ -64,6 +63,7 @@ public interface ConceptManager { /** * Get reasoning concepts detail. + * * @param conceptTypeNames unique names of concept types * @return list of triple semantic */ diff --git a/server/biz/schema/src/main/java/com/antgroup/openspg/server/biz/schema/impl/ConceptManagerImpl.java b/server/biz/schema/src/main/java/com/antgroup/openspg/server/biz/schema/impl/ConceptManagerImpl.java index 03032b986..4c845493e 100644 --- a/server/biz/schema/src/main/java/com/antgroup/openspg/server/biz/schema/impl/ConceptManagerImpl.java +++ b/server/biz/schema/src/main/java/com/antgroup/openspg/server/biz/schema/impl/ConceptManagerImpl.java @@ -20,10 +20,10 @@ import com.antgroup.openspg.core.schema.model.identifier.SPGTypeIdentifier; import com.antgroup.openspg.core.schema.model.semantic.BaseConceptSemantic; import com.antgroup.openspg.core.schema.model.semantic.DynamicTaxonomySemantic; -import com.antgroup.openspg.core.schema.model.semantic.SPGOntologyEnum; -import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.core.schema.model.semantic.LogicalRule; import com.antgroup.openspg.core.schema.model.semantic.RuleStatusEnum; +import com.antgroup.openspg.core.schema.model.semantic.SPGOntologyEnum; +import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.core.schema.model.semantic.request.DefineDynamicTaxonomyRequest; import com.antgroup.openspg.core.schema.model.semantic.request.DefineTripleSemanticRequest; import com.antgroup.openspg.core.schema.model.semantic.request.RemoveDynamicTaxonomyRequest; @@ -149,7 +149,9 @@ public void defineLogicalCausation(DefineTripleSemanticRequest request) { SPGTypeIdentifier.parse(request.getObjectConceptTypeName()), new ConceptIdentifier(request.getObjectConceptName()), logicalRule, - StringUtils.isNotBlank(request.getSemanticType()) ? SPGOntologyEnum.valueOf(request.getSemanticType()) : null); + StringUtils.isNotBlank(request.getSemanticType()) + ? SPGOntologyEnum.valueOf(request.getSemanticType()) + : null); if (conceptSemantic.getOntologyType() != conceptSemantic.getSemanticType()) { conceptSemantic.setOntologyType(conceptSemantic.getSemanticType()); } @@ -178,8 +180,15 @@ public void removeLogicalCausation(RemoveTripleSemanticRequest request) { TripleSemantic conceptSemantic = new TripleSemantic( - subjectType, subjectName, predicateIdentifier, objectType, objectName, null, - StringUtils.isNotBlank(request.getSemanticType()) ? SPGOntologyEnum.valueOf(request.getSemanticType()) : null); + subjectType, + subjectName, + predicateIdentifier, + objectType, + objectName, + null, + StringUtils.isNotBlank(request.getSemanticType()) + ? SPGOntologyEnum.valueOf(request.getSemanticType()) + : null); conceptService.deleteTripleSemantic(conceptSemantic); } diff --git a/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/SPGOntologyEnum.java b/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/SPGOntologyEnum.java index f0bcbfc5c..c8ec14ee7 100644 --- a/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/SPGOntologyEnum.java +++ b/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/SPGOntologyEnum.java @@ -31,9 +31,7 @@ public enum SPGOntologyEnum { /** Concept instance. */ CONCEPT, - /** - * Reasoning concept instance. - */ + /** Reasoning concept instance. */ REASONING_CONCEPT; public static SPGOntologyEnum toEnum(String val) { diff --git a/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/request/DefineTripleSemanticRequest.java b/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/request/DefineTripleSemanticRequest.java index 78017aad8..40c00efa1 100644 --- a/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/request/DefineTripleSemanticRequest.java +++ b/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/request/DefineTripleSemanticRequest.java @@ -38,9 +38,7 @@ public class DefineTripleSemanticRequest extends BaseRequest { /** The dsl content of logic rule defined in spo. */ private String dsl; - /** - * The semantic type of the triple. - */ + /** The semantic type of the triple. */ private String semanticType; public String getSubjectConceptName() { diff --git a/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/request/RemoveTripleSemanticRequest.java b/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/request/RemoveTripleSemanticRequest.java index 2204c5ca9..c015b15c6 100644 --- a/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/request/RemoveTripleSemanticRequest.java +++ b/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/semantic/request/RemoveTripleSemanticRequest.java @@ -35,9 +35,7 @@ public class RemoveTripleSemanticRequest extends BaseRequest { /** The concept name of object in spo triple */ private String objectConceptName; - /** - * The semantic type of the triple. - */ + /** The semantic type of the triple. */ private String semanticType; public String getSubjectConceptName() { diff --git a/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/type/Concept.java b/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/type/Concept.java index 44ed415d1..a84782222 100644 --- a/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/type/Concept.java +++ b/server/core/schema/model/src/main/java/com/antgroup/openspg/core/schema/model/type/Concept.java @@ -16,8 +16,8 @@ import com.antgroup.openspg.core.schema.model.identifier.ConceptIdentifier; import com.antgroup.openspg.core.schema.model.semantic.BaseConceptSemantic; import com.antgroup.openspg.core.schema.model.semantic.DynamicTaxonomySemantic; -import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.core.schema.model.semantic.SystemPredicateEnum; +import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.server.common.model.base.BaseValObj; import java.util.List; import java.util.Objects; diff --git a/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/concept/ConceptSemanticService.java b/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/concept/ConceptSemanticService.java index b846dc11b..56da60862 100644 --- a/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/concept/ConceptSemanticService.java +++ b/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/concept/ConceptSemanticService.java @@ -18,7 +18,6 @@ import com.antgroup.openspg.core.schema.model.semantic.DynamicTaxonomySemantic; import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.server.core.schema.service.semantic.model.TripleSemanticQuery; - import java.util.List; /** diff --git a/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/concept/convertor/ConceptSemanticConvertor.java b/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/concept/convertor/ConceptSemanticConvertor.java index 66d409f3d..25945501c 100644 --- a/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/concept/convertor/ConceptSemanticConvertor.java +++ b/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/concept/convertor/ConceptSemanticConvertor.java @@ -15,10 +15,10 @@ import com.antgroup.openspg.core.schema.model.identifier.ConceptIdentifier; import com.antgroup.openspg.core.schema.model.semantic.DynamicTaxonomySemantic; -import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.core.schema.model.semantic.LogicalRule; import com.antgroup.openspg.core.schema.model.semantic.RuleCode; import com.antgroup.openspg.core.schema.model.semantic.SPGOntologyEnum; +import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.server.core.schema.service.semantic.model.SimpleSemantic; import java.util.ArrayList; import java.util.Collections; diff --git a/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/concept/impl/ConceptSemanticServiceImpl.java b/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/concept/impl/ConceptSemanticServiceImpl.java index fa735b3ed..965412239 100644 --- a/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/concept/impl/ConceptSemanticServiceImpl.java +++ b/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/concept/impl/ConceptSemanticServiceImpl.java @@ -16,18 +16,17 @@ import com.antgroup.openspg.core.schema.model.identifier.ConceptIdentifier; import com.antgroup.openspg.core.schema.model.identifier.SPGTypeIdentifier; import com.antgroup.openspg.core.schema.model.semantic.DynamicTaxonomySemantic; -import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.core.schema.model.semantic.LogicalRule; import com.antgroup.openspg.core.schema.model.semantic.RuleCode; import com.antgroup.openspg.core.schema.model.semantic.SPGOntologyEnum; import com.antgroup.openspg.core.schema.model.semantic.SystemPredicateEnum; +import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.server.core.schema.service.concept.ConceptSemanticService; import com.antgroup.openspg.server.core.schema.service.concept.convertor.ConceptSemanticConvertor; import com.antgroup.openspg.server.core.schema.service.predicate.repository.PropertyRepository; import com.antgroup.openspg.server.core.schema.service.semantic.LogicalRuleService; -import com.antgroup.openspg.server.core.schema.service.semantic.model.TripleSemanticQuery; -import com.antgroup.openspg.server.core.schema.service.semantic.model.ReasoningConclusionQuery; import com.antgroup.openspg.server.core.schema.service.semantic.model.SimpleSemantic; +import com.antgroup.openspg.server.core.schema.service.semantic.model.TripleSemanticQuery; import com.antgroup.openspg.server.core.schema.service.semantic.repository.SemanticRepository; import com.google.common.collect.Lists; import java.util.ArrayList; diff --git a/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/semantic/model/TripleSemanticQuery.java b/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/semantic/model/TripleSemanticQuery.java index 52e8dcd0c..e41ebabd9 100644 --- a/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/semantic/model/TripleSemanticQuery.java +++ b/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/semantic/model/TripleSemanticQuery.java @@ -13,9 +13,8 @@ package com.antgroup.openspg.server.core.schema.service.semantic.model; -import java.util.List; - import com.antgroup.openspg.core.schema.model.semantic.SPGOntologyEnum; +import java.util.List; import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; @@ -41,8 +40,6 @@ public class TripleSemanticQuery { /** The predicate name. */ private String predicateName; - /** - * The type of spg ontology - */ + /** The type of spg ontology */ private SPGOntologyEnum spgOntologyEnum; } diff --git a/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/semantic/repository/SemanticRepository.java b/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/semantic/repository/SemanticRepository.java index 04baad5b3..4c3391c36 100644 --- a/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/semantic/repository/SemanticRepository.java +++ b/server/core/schema/service/src/main/java/com/antgroup/openspg/server/core/schema/service/semantic/repository/SemanticRepository.java @@ -13,10 +13,10 @@ package com.antgroup.openspg.server.core.schema.service.semantic.repository; -import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.core.schema.model.semantic.SPGOntologyEnum; -import com.antgroup.openspg.server.core.schema.service.semantic.model.TripleSemanticQuery; +import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.server.core.schema.service.semantic.model.SimpleSemantic; +import com.antgroup.openspg.server.core.schema.service.semantic.model.TripleSemanticQuery; import java.util.List; /** diff --git a/server/infra/dao/src/main/java/com/antgroup/openspg/server/infra/dao/repository/schema/SemanticRepositoryImpl.java b/server/infra/dao/src/main/java/com/antgroup/openspg/server/infra/dao/repository/schema/SemanticRepositoryImpl.java index 889ff19e7..bf223b97e 100644 --- a/server/infra/dao/src/main/java/com/antgroup/openspg/server/infra/dao/repository/schema/SemanticRepositoryImpl.java +++ b/server/infra/dao/src/main/java/com/antgroup/openspg/server/infra/dao/repository/schema/SemanticRepositoryImpl.java @@ -15,11 +15,11 @@ import com.antgroup.openspg.common.util.CollectionsUtils; import com.antgroup.openspg.common.util.StringUtils; -import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.core.schema.model.semantic.SPGOntologyEnum; +import com.antgroup.openspg.core.schema.model.semantic.TripleSemantic; import com.antgroup.openspg.server.common.service.SequenceRepository; -import com.antgroup.openspg.server.core.schema.service.semantic.model.TripleSemanticQuery; import com.antgroup.openspg.server.core.schema.service.semantic.model.SimpleSemantic; +import com.antgroup.openspg.server.core.schema.service.semantic.model.TripleSemanticQuery; import com.antgroup.openspg.server.core.schema.service.semantic.repository.SemanticRepository; import com.antgroup.openspg.server.infra.dao.dataobject.SemanticDO; import com.antgroup.openspg.server.infra.dao.dataobject.SemanticDOExample;