Drools 检查列表中的 属性 是否存在于其他列表中

Drools check if one property from list is present in other list

我得到了以下结构:

public class Proposal {
   private final List<Product> products; 
   private final List<Customer> customers;
}

public class Customer {
   private String id;
   private String country;
}

public class Product {
  private String customerId;
  private String country;
  private String type; 
}

要求是: 不要检查产品类型是“A”还是“B”以及 ID 为 21 的客户。 对于任何其他产品,请检查 customerId 是否与 id 匹配,或者客户国家/地区是否设置为 null 并且产品上的国家/地区设置为 'US'

基于此,我准备了一个规则,但它无法正常工作:

//rule body
Proposal($products: products, $customers: customers)

$productsToChecks : Product(customerId != "21") && type not in ("A", "B") from $products

//first condition to check if there is any applicable case:
exist(Product(customerId != "21") && type not in ("A", "B") from $products) and

(
  // check if there is a customer who can use this product
  forall (
    Product($customerId: customerId, $country: country) from $productsToChecks
    Customer(id == $customerId || (country == null &&  $country == "US"))

  )
)

感谢您的任何帮助或建议

您的 $productsToChecks 声明中存在语法错误:

$productsToChecks : Product(customerId != "21") && type not in ("A", "B") from $products

您要检查的两个属性都需要在 Product( ... ) 部分内,如下所示:

$productsToChecks: Product( customerId != "21",
                            type not in ("A", "B")) from $products

您在规则的其他部分也重复了此错误。


所以你的要求是:

Don't check if the product type is "A" or "B" and for customer with id: 21. For any other products check if customerId match with id or if the customer country is set to null and the country on the product is set to 'US'

我们可以将其提炼为以下 pseudo-code:

    • 产品不是:(type=A 或 type=B)和 customerId=21
    • (产品客户 ID == 客户 ID)或(客户国家 == 空和产品国家 == 美国)
  • 然后做点什么

鉴于第二部分的'OR',这是两条规则。

我们需要做的第一部分是找到我们关心的产品子集。您可以通过多种方式执行此操作——collectaccumulate 是您会立即想到的两种方式。假设你的问题中的要求是完整的,collect在这里更合适(也更简单)。

Proposal($products: products, $customers: customers)

$productSubset: List() from collect( Product( customerId != 21, type not in ("A", "B") ) from $products)

现在您可以使用该产品子集(不包括您需要忽略的产品)来匹配您的其他条件。正如我所提到的,由于这些标准是或运算的,因此它们应该是两个不同的规则。

rule "Product customerId matches Customer id"
when
  Proposal($products: products, $customers: customers)
  $productSubset: List() 
                  from collect( Product( customerId != 21, type not in ("A", "B") ) from $products)

  Customer( $id: id != null ) from $customers
  $product: Product( customerId == $id ) from $productSubset
then
  // do something with $product
end

rule "US Product and no Customer Country"
when
  Proposal($products: products, $customers: customers)
  $productSubset: List() 
                  from collect( Product( customerId != 21, type not in ("A", "B") ) from $products)

  Customer( country == null ) from $customers
  $product: Product( country == "US" ) from $productSubset
then
  // do something with $product
end

要减少重复代码,您可以将通用条件拉入单个 'parent' 规则,然后使用 extends 关键字创建两个具有不同条件的子规则。

我以这种方式设计这些规则的前提是,您希望对符合您标准的每个产品执行一些操作。基于此假设,右侧将触发每个符合每个规则条件的产品(另请注意,由于这两个规则不排斥,如果 customerId 匹配 和 [=60,产品可能会触发两次=] 满足国家要求。)

但是,如果您想要的结果是满足条件的所有产品的列表,您可以再次使用函数来获取该产品列表。在这种情况下,accumulate 函数比 collect:

更合适
rule "Get list of products for customer"
when
  Proposal($products: products, $customers: customers)
  $productSubset: List() 
                  from collect( Product( customerId != 21, type not in ("A", "B") ) from $products)

  Customer( $id: id != null, $country: country ) from $customers

  $product: Product( customerId == $id ) from $productSubset

  $customerProducts: List() from accumulate(
                       $p: Product((customerId == $id) || ($country == null && country == "US")) from $products,
                       collectList($p)
                     )
then
  // do something with $customerProducts
end