This is an automated email from the ASF dual-hosted git repository.
tkobayas pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie.git
The following commit(s) were added to refs/heads/main by this push:
new 44d65d5eee0 [incubator-kie#7222] Do not create duplicate facts on
insert in sequential mode (#7250)
44d65d5eee0 is described below
commit 44d65d5eee0a1876d11c32606b8dfbb7ad29d9d4
Author: Toshiya Kobayashi <[email protected]>
AuthorDate: Wed Nov 4 10:08:17 2026 +0900
[incubator-kie#7222] Do not create duplicate facts on insert in sequential
mode (#7250)
Return the existing handle if the object is already in the working
memory, as in non-sequential mode. Also fix the tests for pending
matches to assert the sequential mode semantics.
---
.../kiesession/entrypoints/NamedEntryPoint.java | 6 ++
.../SequentialInsertDeleteUpdateTest.java | 105 ++++++++++++++++++---
2 files changed, 100 insertions(+), 11 deletions(-)
diff --git
a/drools-kiesession/src/main/java/org/drools/kiesession/entrypoints/NamedEntryPoint.java
b/drools-kiesession/src/main/java/org/drools/kiesession/entrypoints/NamedEntryPoint.java
index 874e9d7ec48..3fc3599f14c 100644
---
a/drools-kiesession/src/main/java/org/drools/kiesession/entrypoints/NamedEntryPoint.java
+++
b/drools-kiesession/src/main/java/org/drools/kiesession/entrypoints/NamedEntryPoint.java
@@ -196,6 +196,12 @@ public class NamedEntryPoint implements
InternalWorkingMemoryEntryPoint, Propert
null,
entryPoint);
if ( this.reteEvaluator.isSequential() ) {
+ // check if the object already exists in the WM
+ final InternalFactHandle existingHandle =
this.objectStore.getHandleForObject( object );
+ if ( existingHandle != null ) {
+ return existingHandle;
+ }
+
InternalFactHandle handle = createHandle( object, typeConf );
propagationContext.setFactHandle(handle);
insert( handle, object, rule, typeConf, propagationContext );
diff --git
a/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/sequential/SequentialInsertDeleteUpdateTest.java
b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/sequential/SequentialInsertDeleteUpdateTest.java
index e07cb13599d..9400713c144 100644
---
a/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/sequential/SequentialInsertDeleteUpdateTest.java
+++
b/drools-test-coverage/test-compiler-integration/src/test/java/org/drools/mvel/integrationtests/sequential/SequentialInsertDeleteUpdateTest.java
@@ -29,11 +29,11 @@ import
org.drools.testcoverage.common.util.KieBaseTestConfiguration;
import org.drools.testcoverage.common.util.KieBaseUtil;
import org.drools.testcoverage.common.util.KieUtil;
import org.drools.testcoverage.common.util.TestParametersUtil2;
-import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.kie.api.KieBase;
import org.kie.api.builder.KieModule;
+import org.kie.api.conf.EqualityBehaviorOption;
import org.kie.api.conf.SequentialOption;
import org.kie.api.runtime.StatelessKieSession;
import org.kie.internal.command.CommandFactory;
@@ -141,11 +141,10 @@ public class SequentialInsertDeleteUpdateTest {
}
/**
- * Inserting an object which is already in the working memory.
+ * Inserting an object which is already in the working memory doesn't
create another fact.
*/
@ParameterizedTest(name = "KieBase type={0}")
@MethodSource("parameters")
- @Disabled("Behaves differently from non-sequential mode. See
https://github.com/apache/incubator-kie/issues/7222")
public void testInsertSameObjectAgain(KieBaseTestConfiguration
kieBaseTestConfiguration) {
String str =
"package org.drools.mvel.compiler.test\n" +
@@ -167,6 +166,79 @@ public class SequentialInsertDeleteUpdateTest {
assertThat(list).containsExactly("R1", "R2:stilton");
}
+ /**
+ * 2 Persons share a Cheese, and R1 inserts it for each Person. Only the
first insert creates a fact.
+ */
+ @ParameterizedTest(name = "KieBase type={0}")
+ @MethodSource("parameters")
+ public void testInsertSharedObjectInRhs(KieBaseTestConfiguration
kieBaseTestConfiguration) {
+ String str =
+ "package org.drools.mvel.compiler.test\n" +
+ "import " + Person.class.getCanonicalName() + "\n" +
+ "import " + Cheese.class.getCanonicalName() + "\n" +
+ "global " + List.class.getCanonicalName() + " list\n" +
+ "rule R1 salience 100 when\n" +
+ " $p : Person()\n" +
+ "then\n" +
+ " insert( $p.getCheese() );\n" +
+ "end\n" +
+ "rule R2 when\n" +
+ " $c : Cheese()\n" +
+ "then\n" +
+ " list.add( \"R2:\" + $c.getType() );\n" +
+ "end\n";
+
+ Cheese cheese = new Cheese("stilton", 10);
+ Person p1 = new Person("p1", "stilton", 0);
+ p1.setCheese(cheese);
+ Person p2 = new Person("p2", "stilton", 0);
+ p2.setCheese(cheese);
+
+ List<String> list = executeSequential(str, kieBaseTestConfiguration,
p1, p2);
+ assertThat(list).containsExactly("R2:stilton");
+ }
+
+ /**
+ * The same object is passed twice as an initial fact. It doesn't depend
on the RHS.
+ */
+ @ParameterizedTest(name = "KieBase type={0}")
+ @MethodSource("parameters")
+ public void
testInsertSameObjectTwiceAsInitialFact(KieBaseTestConfiguration
kieBaseTestConfiguration) {
+ String str =
+ "package org.drools.mvel.compiler.test\n" +
+ "import " + Cheese.class.getCanonicalName() + "\n" +
+ "global " + List.class.getCanonicalName() + " list\n" +
+ "rule R1 when\n" +
+ " $c : Cheese()\n" +
+ "then\n" +
+ " list.add( \"R1:\" + $c.getType() );\n" +
+ "end\n";
+
+ Cheese cheese = new Cheese("stilton", 10);
+ List<String> list = executeSequential(str, kieBaseTestConfiguration,
cheese, cheese);
+ assertThat(list).containsExactly("R1:stilton");
+ }
+
+ /**
+ * With equality behavior, an object equal to one in the working memory
doesn't create another fact.
+ */
+ @ParameterizedTest(name = "KieBase type={0}")
+ @MethodSource("parameters")
+ public void
testInsertEqualObjectWithEqualityBehavior(KieBaseTestConfiguration
kieBaseTestConfiguration) {
+ String str =
+ "package org.drools.mvel.compiler.test\n" +
+ "import " + Cheese.class.getCanonicalName() + "\n" +
+ "global " + List.class.getCanonicalName() + " list\n" +
+ "rule R1 when\n" +
+ " $c : Cheese()\n" +
+ "then\n" +
+ " list.add( \"R1:\" + $c.getType() );\n" +
+ "end\n";
+
+ List<String> list = executeSequentialWithEquality(str,
kieBaseTestConfiguration, new Cheese("stilton", 10), new Cheese("stilton", 10));
+ assertThat(list).containsExactly("R1:stilton");
+ }
+
/**
* JoinNode - right insert: R1 inserts a Cheese which joins with the
existing Person in R2.
*/
@@ -373,11 +445,11 @@ public class SequentialInsertDeleteUpdateTest {
/**
* A single-use Coupon is deleted when it is applied. R1 has 2 matches
(one per Order) with the same Coupon.
- * The first firing deletes the Coupon, so the other match is cancelled as
it is in non-sequential mode.
+ * The first firing deletes the Coupon, but R1 is not re-evaluated, so the
other match is not cancelled.
+ * It differs from non-sequential mode, where the Coupon is applied to
only 1 Order.
*/
@ParameterizedTest(name = "KieBase type={0}")
@MethodSource("parameters")
- @Disabled("Behaves differently from non-sequential mode. See
https://github.com/apache/incubator-kie/issues/7222")
public void
testDeleteFactOfPendingMatchOfSameRule(KieBaseTestConfiguration
kieBaseTestConfiguration) {
String str =
"package org.drools.mvel.compiler.test\n" +
@@ -396,8 +468,8 @@ public class SequentialInsertDeleteUpdateTest {
Order o1 = new Order("o1", 100);
Order o2 = new Order("o2", 100);
List<String> list = executeSequential(str, kieBaseTestConfiguration,
o1, o2, new Coupon("A", 10));
- assertThat(list).hasSize(1);
- assertThat(Stream.of(o1, o2).filter(Order::isDiscounted)).hasSize(1);
+ assertThat(list).containsExactly("R1:o1:A", "R1:o2:A");
+ assertThat(Stream.of(o1, o2).filter(Order::isDiscounted)).hasSize(2);
}
/**
@@ -674,11 +746,11 @@ public class SequentialInsertDeleteUpdateTest {
/**
* Only one Coupon can be applied to an Order. R1 has 2 matches (one per
Coupon) with the same Order.
- * The first firing updates the Order to discounted, so the other match is
cancelled as it is in non-sequential mode.
+ * The first firing updates the Order to discounted, but R1 is not
re-evaluated, so the other match is not cancelled.
+ * It differs from non-sequential mode, where only 1 Coupon is applied.
*/
@ParameterizedTest(name = "KieBase type={0}")
@MethodSource("parameters")
- @Disabled("Behaves differently from non-sequential mode. See
https://github.com/apache/incubator-kie/issues/7222")
public void
testUpdateFactOfPendingMatchOfSameRule(KieBaseTestConfiguration
kieBaseTestConfiguration) {
String str =
"package org.drools.mvel.compiler.test\n" +
@@ -695,8 +767,8 @@ public class SequentialInsertDeleteUpdateTest {
Order o1 = new Order("o1", 100);
List<String> list = executeSequential(str, kieBaseTestConfiguration,
o1, new Coupon("A", 10), new Coupon("B", 20));
- assertThat(list).hasSize(1);
- assertThat(o1.getTotal()).isIn(90, 80);
+ assertThat(list).containsExactly("R1:o1:A", "R1:o1:B");
+ assertThat(o1.getTotal()).isEqualTo(70);
}
/**
@@ -989,6 +1061,17 @@ public class SequentialInsertDeleteUpdateTest {
final KieModule kieModule = KieUtil.getKieModuleFromDrls("test",
kieBaseTestConfiguration, drl);
final KieBase kbase =
KieBaseUtil.newKieBaseFromKieModuleWithAdditionalOptions(
kieModule, kieBaseTestConfiguration, SequentialOption.YES);
+ return execute(kbase, facts);
+ }
+
+ private List<String> executeSequentialWithEquality(String drl,
KieBaseTestConfiguration kieBaseTestConfiguration, Object... facts) {
+ final KieModule kieModule = KieUtil.getKieModuleFromDrls("test",
kieBaseTestConfiguration, drl);
+ final KieBase kbase =
KieBaseUtil.newKieBaseFromKieModuleWithAdditionalOptions(
+ kieModule, kieBaseTestConfiguration, SequentialOption.YES,
EqualityBehaviorOption.EQUALITY);
+ return execute(kbase, facts);
+ }
+
+ private List<String> execute(KieBase kbase, Object... facts) {
StatelessKieSession ksession = kbase.newStatelessKieSession();
final List<String> list = new ArrayList<>();
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]