可配置物料清单 (CBOM)

1. 简介

在制造和产品设计这个动态领域中,管理可配置物料清单(CBOM,通常称为 150% BOM)对于处理产品变体、定制化和供应链效率至关重要。可配置物料清单用例解决了一个核心业务挑战:如何在遵守约束条件、评分标准和依赖关系的前提下,从组件超集中声明式地解析出最优产品配置。通过利用 Neo4j 的 Cypher 25,企业可以建模复杂的图结构来遍历和剪枝变体选项,从而确保产品构建具有成本效益、轻量化且合规。这些能力支持更好的库存管理、加快定制产品的上市时间,并降低制造环境中的运营风险。

2. 应用场景

要理解可配置物料清单用例的重要性,可以考虑制造业中因变体管理不善而导致效率低下和错误的实际挑战。以下三个关键领域说明了这些问题:

  1. 产品定制与变体管理

    • 客户需要个性化的产品,但在没有结构化解析的情况下,选择兼容组件会变得混乱。

    • 过于广泛的选项集(150% BOM)可能导致无效配置,从而延误生产并增加浪费。

    • 缺乏对依赖关系的可见性通常会导致装配线中出现被忽略的不兼容问题。

  2. 成本与资源优化

    • 如果不自动剪除次优分支,就很难在各个变体之间平衡成本、重量和性能。

    • 手动配置过程容易出错,导致更高的材料成本或更重的产品。

    • 传统系统难以应对动态约束,增加了最小化供应链风险的难度。

  3. 合规性与质量保证

这些场景凸显了采用 Neo4j 结合 Cypher 25 的可配置物料清单等先进解决方案的必要性,该方案利用图技术对产品变体进行建模、解析和可视化,为制造业的业务和技术用户提供必要的洞察。

cbom bike gemini
图 1. Gemini 生成的山地车 CBOM 示例

3. 解决方案

像 Neo4j 这样的先进图数据库对于驾驭制造业中互联产品数据的复杂性至关重要。它们擅长表示层次结构、依赖关系和可配置选项,使声明式解析变体变得容易。这意味着您可以通过简单地描述关系和约束,而不是编写复杂的程序代码,来要求数据库解析配置变体(如有效的产品组合或依赖关系)。通过将数据建模为图,企业可以应用约束、对选项进行评分并剪枝路径,从而改善决策制定、定制效率和制造敏捷性。

3.1. 图数据库如何提供帮助?

图数据库为制造业中可配置物料清单的挑战提供了强大的解决方案。以下是图数据库不可或缺的五个关键原因:

  1. 层次结构建模: 图自然地捕捉了装配体、零件和配置组及其关系,有效地处理了 150% BOM 的超集特性。

  2. 基于约束的剪枝: 它们能够实时应用允许/禁止列表以及其他规则,以便在遍历过程中消除无效配置。

  3. 评分与优化: 图支持多因素评分(例如成本、重量),以便从并发选项中选择最优分支。

  4. 变体解析与可视化: 它们为解析后的 BOM 提供了清晰的路径,揭示了依赖关系和未满足的需求。

  5. 灵活的约束处理: 图简化了对宽松、严格约束或过度约束场景的管理,确保合规性和可扩展性。

这些特性使得图数据库成为制造业企业获取洞察并解决可配置物料清单中复杂问题的关键。

4. 建模

本节描述了一个示例图数据模型并提供了数据摄取脚本。目标是指导生产中的数据建模,并设置一个包含多个节点的小型图,这些图将用于演示后续章节中的 Cypher 查询结构。

4.1. 数据模型

cbom model
图 2. CBOM 数据模型
cbom resolved bom model
图 3. 解析后的 BOM 数据模型

4.1.1 所需数据字段

以下是开始所需的字段

  • 产品节点 (Product Node)

    • id: 产品的唯一标识符(例如 "MB1")

    • desc: 产品描述

  • 装配节点 (Assembly Node)

    • id: 装配体的唯一标识符(例如 "A_Drivetrain")

    • desc: 装配体描述

    • 类别特定的属性(例如,轮组装配的 wheel_size

  • 配置组节点 (ConfigGroup Node)

    • id: 配置组的唯一标识符(例如 "CG_Gear_Sys")

    • category: 选项类别(例如 "gear_system")

    • options_properties: 选项属性列表(例如 ["cost", "weight", "gear_system"])

    • desc: 配置组描述

  • 零件节点 (Part Node)

    • id: 零件的唯一标识符(例如 "P_Gear_7")

    • desc: 零件描述

    • cost: 零件成本

    • weight: 零件重量

    • 各种特定类别的属性(例如 gear_system, material, pattern

  • HAS_PART 关系

    • qty: 所需数量

  • REQUIRES 关系

    • qty: 所需数量

    • note: 附加说明(例如 "选择一个传动系统")

  • HAS_OPTION 关系

    • (该模型中没有额外属性)

4.1.2 所需参数

使用三组参数来演示不同的约束场景:

  • 良好约束变体(导致完美解析的 BOM)

    :params {
        id_variant: "awesome_bike_well_constrained", 
        scoring: [
            {
              field: "cost",
              factor: -1000
            },
            {
              field: "weight",
              factor: -1
            }
        ],
        constraints: [
            {
                category: "wheel_size",
                properties: [
                    {
                        name: "wheel_size",
                        type: "float",
                        description: "Size of the wheel in inches",
                        allow_list: [
                            26.0
                        ]
                    }
                ]
            },
            {
                category: "rim",
                properties: [
                    {
                        name: "material",
                        type: "string",
                        description: "material of the rim",
                        allow_list: [
                            "Carbon"
                        ]
                    }
                ]
            },
            {
                category: "tire",
                properties: [
                    {
                        name: "pattern",
                        type: "string",
                        description: "Type of tire",
                        allow_list: [
                            "Knobby"
                        ]
                    }
                ]
            },
            {
                category: "frame_material",
                properties: [
                    {
                        name: "material",
                        type: "string",
                        description: "frame material",
                        deny_list: [
                            "Carbon Fiber", "Steel"
                        ]
                    }
                ]
            },
            {
                category: "color",
                properties: [
                    {
                        name: "color",
                        type: "string",
                        description: "color of the bike",
                        allow_list: [
                           "Black Paint"
                        ]
                    }
                ]
            },
            {
                category: "caliper",
                properties: [
                    {
                        name: "caliper",
                        type: "string",
                        description: "Type of caliper",
                        allow_list: [
                            "Mechanical"
                        ]
                    }
                ]
            },
            {
                category: "shifter",
                properties: [
                    {
                        name: "shifter",
                        type: "string",
                        description: "Type of Shifter",
                        allow_list: [
                            "Trigger"
                        ]
                    }
                ]
            },
            {
                category: "derailleur",
                properties: [
                    {
                        name: "derailleur",
                        type: "string",
                        description: "Type of Derailleur",
                        allow_list: [
                            "Advanced"
                        ]
                    }
                ]
            },
            {
                category: "gear_system",
                properties: [
                    {
                        name: "gear_system",
                        type: "string",
                        description: "gear system type",
                        allow_list: [
                            "12-Speed"
                        ]
                    }
                ]
            }
        ]
    }
  • 宽松约束变体(通过基于评分的剪枝解析并发分支)

    :params {
        id_variant: "awesome_bike_loose_constraints", 
        scoring: [
            {
              field: "cost",
              factor: -1000
            },
            {
              field: "weight",
              factor: -1
            }
        ],
        constraints: [
            {
                category: "wheel_size",
                properties: [
                    {
                        name: "wheel_size",
                        type: "float",
                        description: "Size of the wheel in inches",
                        deny_list: [
                            26.0
                        ]
                    }
                ]
            },
            {
                category: "rim",
                properties: [
                    {
                        name: "material",
                        type: "string",
                        description: "material of the rim",
                        allow_list: [
                            "Carbon"
                        ]
                    }
                ]
            },
            {
                category: "tire",
                properties: [
                    {
                        name: "pattern",
                        type: "string",
                        description: "Type of tire",
                        allow_list: [
                            "Knobby"
                        ]
                    }
                ]
            },
            {
                category: "frame_material",
                properties: [
                    {
                        name: "material",
                        type: "string",
                        description: "frame material",
                        deny_list: [
                            "Carbon Fiber"
                        ]
                    }
                ]
            },
            {
                category: "color",
                properties: [
                    {
                        name: "color",
                        type: "string",
                        description: "color of the bike",
                        allow_list: [
                            "Black Paint"
                        ]
                    }
                ]
            },
            {
                category: "caliper",
                properties: [
                    {
                        name: "caliper",
                        type: "string",
                        description: "Type of caliper",
                        allow_list: [
                            "Mechanical"
                        ]
                    }
                ]
            },
            {
                category: "shifter",
                properties: [
                    {
                        name: "shifter",
                        type: "string",
                        description: "Type of Shifter",
                        allow_list: [
                            "Trigger"
                        ]
                    }
                ]
            },
            {
                category: "derailleur",
                properties: [
                    {
                        name: "derailleur",
                        type: "string",
                        description: "Type of Derailleur",
                        allow_list: [
                            "Advanced"
                        ]
                    }
                ]
            },
            {
                category: "gear_system",
                properties: [
                    {
                        name: "gear_system",
                        type: "string",
                        description: "gear system type",
                        deny_list: [
                            "7-Speed"
                        ]
                    }
                ]
            }
        ]
    }
  • 过度约束变体(某些需求未得到满足,例如没有颜色可选)

    :params {
        id_variant: "awesome_bike_no_color", 
        scoring: [
            {
              field: "cost",
              factor: -1000
            },
            {
              field: "weight",
              factor: -1
            }
        ],
        constraints: [
            {
                category: "wheel_size",
                properties: [
                    {
                        name: "wheel_size",
                        type: "float",
                        description: "Size of the wheel in inches",
                        allow_list: [
                            26.0
                        ]
                    }
                ]
            },
            {
                category: "rim",
                properties: [
                    {
                        name: "material",
                        type: "string",
                        description: "material of the rim",
                        allow_list: [
                            "Carbon"
                        ]
                    }
                ]
            },
            {
                category: "tire",
                properties: [
                    {
                        name: "pattern",
                        type: "string",
                        description: "Type of tire",
                        allow_list: [
                            "Knobby"
                        ]
                    }
                ]
            },
            {
                category: "frame_material",
                properties: [
                    {
                        name: "material",
                        type: "string",
                        description: "frame material",
                        deny_list: [
                            "Carbon Fiber"
                        ]
                    }
                ]
            },
            {
                category: "color",
                properties: [
                    {
                        name: "color",
                        type: "string",
                        description: "color of the bike",
                        allow_list: [
                        ]
                    }
                ]
            },
            {
                category: "caliper",
                properties: [
                    {
                        name: "caliper",
                        type: "string",
                        description: "Type of caliper",
                        allow_list: [
                            "Mechanical"
                        ]
                    }
                ]
            },
            {
                category: "shifter",
                properties: [
                    {
                        name: "shifter",
                        type: "string",
                        description: "Type of Shifter",
                        allow_list: [
                            "Trigger"
                        ]
                    }
                ]
            },
            {
                category: "derailleur",
                properties: [
                    {
                        name: "derailleur",
                        type: "string",
                        description: "Type of Derailleur",
                        allow_list: [
                            "Advanced"
                        ]
                    }
                ]
            },
            {
                category: "gear_system",
                properties: [
                    {
                        name: "gear_system",
                        type: "string",
                        description: "gear system type",
                        allow_list: [
                            "12-Speed"
                        ]
                    }
                ]
            }
        ]
    }

4.2. 演示数据

以下 Cypher 语句将在 Neo4j 数据库中创建示例图:

CREATE CONSTRAINT id_Assembly_uniq IF NOT EXISTS FOR (node:Assembly) REQUIRE (node.id) IS UNIQUE;
CREATE CONSTRAINT id_ConfigGroup_uniq IF NOT EXISTS FOR (node:ConfigGroup) REQUIRE (node.id) IS UNIQUE;
CREATE CONSTRAINT id_Part_uniq IF NOT EXISTS FOR (node:Part) REQUIRE (node.id) IS UNIQUE;
CREATE CONSTRAINT id_Product_uniq IF NOT EXISTS FOR (node:Product) REQUIRE (node.id) IS UNIQUE;

CALL db.awaitIndexes(300);

UNWIND [{id:"MB1", properties:{desc:"Configurable Mountain Bike"}}] AS row
CREATE (n:Product{id: row.id}) SET n += row.properties;
UNWIND [{id:"A_Drivetrain", properties:{desc:"Drivetrain Assembly"}}, {id:"A_Wheel", properties:{desc:"Wheel Assembly"}}, {id:"A_Wheel_26", properties:{wheel_size:26.0, desc:"26\" Wheel Set"}}, {id:"A_Wheel_275", properties:{wheel_size:27.5, desc:"27.5\" Wheel Set"}}, {id:"A_Wheel_29", properties:{wheel_size:29.0, desc:"29\" Wheel Set"}}, {id:"A_Frame", properties:{desc:"Frame Assembly"}}, {id:"A_Brakes", properties:{desc:"Disc Brakes Assembly"}}] AS row
CREATE (n:Assembly{id: row.id}) SET n += row.properties;
UNWIND [{id:"CG_Gear_Sys", properties:{category:"gear_system", options_properties:["cost", "weight", "gear_system"], desc:"Gear System Options"}}, {id:"CG_Shifter", properties:{category:"shifter", options_properties:["cost", "weight", "shifter"], desc:"Shifter Options"}}, {id:"CG_Derail", properties:{category:"derailleur", options_properties:["cost", "weight", "derailleur"], desc:"Derailleur Options"}}, {id:"CG_Caliper", properties:{category:"caliper", options_properties:["cost", "weight", "caliper"], desc:"Caliper Options"}}, {id:"CG_Wheel_Size", properties:{category:"wheel_size", options_properties:["wheel_size"], desc:"Wheel Size Options"}}, {id:"CG_Rim_26", properties:{category:"rim", options_properties:["cost", "weight", "material"], desc:"Rim Options for 26\""}}, {id:"CG_Tire_26", properties:{category:"tire", options_properties:["cost", "weight", "pattern"], desc:"Tire Options for 26\""}}, {id:"CG_Rim_275", properties:{category:"rim", options_properties:["cost", "weight", "material"], desc:"Rim Options for 27.5\""}}, {id:"CG_Tire_275", properties:{category:"tire", options_properties:["cost", "weight", "pattern"], desc:"Tire Options for 27.5\""}}, {id:"CG_Rim_29", properties:{category:"rim", options_properties:["cost", "weight", "material"], desc:"Rim Options for 29\""}}, {id:"CG_Tire_29", properties:{category:"tire", options_properties:["cost", "weight", "pattern"], desc:"Tire Options for 29\""}}, {id:"CG_Frame_Mat", properties:{category:"frame_material", options_properties:["cost", "weight", "material"], desc:"Frame Material Options"}}, {id:"CG_Color", properties:{category:"color", options_properties:["cost", "weight", "color"], desc:"Color Options"}}] AS row
CREATE (n:ConfigGroup{id: row.id}) SET n += row.properties;
UNWIND [{id:"P_Gear_7", properties:{gear_system:"7-Speed", cost:100.0, weight:0.8, desc:"7-Speed Gear System"}}, {id:"P_Gear_11", properties:{gear_system:"11-Speed", cost:200.0, weight:0.75, desc:"11-Speed Gear System"}}, {id:"P_Gear_12", properties:{gear_system:"12-Speed", cost:250.0, weight:0.7, desc:"12-Speed Gear System"}}, {id:"P_Shifter_Twist", properties:{cost:30.0, shifter:"Twist Grip", weight:0.15, desc:"Twist Grip Shifter"}}, {id:"P_Shifter_Trigger", properties:{cost:50.0, shifter:"Trigger", weight:0.18, desc:"Trigger Shifter"}}, {id:"P_Derail_Basic", properties:{cost:40.0, weight:0.25, derailleur:"Basic", desc:"Basic Derailleur"}}, {id:"P_Derail_Adv", properties:{cost:80.0, weight:0.22, derailleur:"Advanced", desc:"Advanced Derailleur"}}, {id:"P_Spokes", properties:{cost:20.0, weight:0.3, desc:"Steel Spokes (36-pack)"}}, {id:"P_Saddle", properties:{cost:30.0, weight:0.25, desc:"Padded Saddle"}}, {id:"P_Pedals", properties:{cost:50.0, weight:0.4, desc:"Clipless Pedals"}}, {id:"P_Chain", properties:{cost:25.0, weight:0.28, desc:"Bike Chain"}}, {id:"P_Cal_Mech", properties:{caliper:"Mechanical", cost:20.0, weight:0.2, desc:"Mechanical Caliper"}}, {id:"P_Cal_Hyd", properties:{caliper:"Hydraulic", cost:40.0, weight:0.18, desc:"Hydraulic Caliper"}}, {id:"P_Rim_Alloy_26", properties:{cost:45.0, material:"Alloy", weight:0.45, desc:"Alloy Rim for 26\""}}, {id:"P_Rim_Carbon_26", properties:{cost:140.0, material:"Carbon", weight:0.35, desc:"Carbon Rim for 26\""}}, {id:"P_Tire_Knobby_26", properties:{cost:35.0, pattern:"Knobby", weight:0.7, desc:"Knobby Tire for 26\""}}, {id:"P_Tire_Slick_26", properties:{cost:30.0, pattern:"Slick", weight:0.6, desc:"Slick Tire for 26\""}}, {id:"P_Rim_Alloy_275", properties:{cost:50.0, material:"Alloy", weight:0.5, desc:"Alloy Rim for 27.5\""}}, {id:"P_Rim_Carbon_275", properties:{cost:150.0, material:"Carbon", weight:0.4, desc:"Carbon Rim for 27.5\""}}, {id:"P_Tire_Knobby_275", properties:{cost:40.0, pattern:"Knobby", weight:0.75, desc:"Knobby Tire for 27.5\""}}] AS row
CREATE (n:Part{id: row.id}) SET n += row.properties;
UNWIND [{id:"P_Tire_Slick_275", properties:{cost:35.0, pattern:"Slick", weight:0.65, desc:"Slick Tire for 27.5\""}}, {id:"P_Rim_Alloy_29", properties:{cost:55.0, material:"Alloy", weight:0.55, desc:"Alloy Rim for 29\""}}, {id:"P_Rim_Carbon_29", properties:{cost:160.0, material:"Carbon", weight:0.45, desc:"Carbon Rim for 29\""}}, {id:"P_Tire_Knobby_29", properties:{cost:45.0, pattern:"Knobby", weight:0.8, desc:"Knobby Tire for 29\""}}, {id:"P_Tire_Slick_29", properties:{cost:40.0, pattern:"Slick", weight:0.7, desc:"Slick Tire for 29\""}}, {id:"P_Frame_Alum", properties:{cost:200.0, material:"Aluminum", weight:2.5, desc:"Aluminum Frame"}}, {id:"P_Frame_Carbon", properties:{cost:500.0, material:"Carbon Fiber", weight:1.5, desc:"Carbon Fiber Frame"}}, {id:"P_Frame_Steel", properties:{cost:150.0, material:"Steel", weight:3.0, desc:"Steel Frame"}}, {id:"P_Color_Black", properties:{cost:0.0, color:"Black Paint", weight:0.05, desc:"Black Paint"}}, {id:"P_Color_Blue", properties:{cost:10.0, color:"Blue Paint", weight:0.05, desc:"Blue Paint"}}, {id:"P_Color_Green", properties:{cost:10.0, color:"Green Paint", weight:0.05, desc:"Green Paint"}}] AS row
CREATE (n:Part{id: row.id}) SET n += row.properties;

UNWIND [{start: {id:"A_Drivetrain"}, end: {id:"P_Chain"}, properties:{qty:1}}, {start: {id:"A_Wheel"}, end: {id:"P_Spokes"}, properties:{qty:1}}] AS row
MATCH (start:Assembly{id: row.start.id})
MATCH (end:Part{id: row.end.id})
CREATE (start)-[r:HAS_PART]->(end) SET r += row.properties;
UNWIND [{start: {id:"A_Drivetrain"}, end: {id:"CG_Shifter"}, properties:{note:"Select one shifter type", qty:1}}, {start: {id:"A_Drivetrain"}, end: {id:"CG_Derail"}, properties:{note:"Select one derailleur", qty:1}}, {start: {id:"A_Wheel"}, end: {id:"CG_Wheel_Size"}, properties:{note:"Select one wheel size", qty:1}}, {start: {id:"A_Wheel_26"}, end: {id:"CG_Rim_26"}, properties:{note:"Select rim for 26\"", qty:1}}, {start: {id:"A_Wheel_26"}, end: {id:"CG_Tire_26"}, properties:{note:"Select tire for 26\"", qty:1}}, {start: {id:"A_Wheel_275"}, end: {id:"CG_Rim_275"}, properties:{note:"Select rim for 27.5\"", qty:1}}, {start: {id:"A_Wheel_275"}, end: {id:"CG_Tire_275"}, properties:{note:"Select tire for 27.5\"", qty:1}}, {start: {id:"A_Wheel_29"}, end: {id:"CG_Rim_29"}, properties:{note:"Select rim for 29\"", qty:1}}, {start: {id:"A_Wheel_29"}, end: {id:"CG_Tire_29"}, properties:{note:"Select tire for 29\"", qty:1}}, {start: {id:"A_Frame"}, end: {id:"CG_Frame_Mat"}, properties:{note:"Select one frame material", qty:1}}, {start: {id:"A_Frame"}, end: {id:"CG_Color"}, properties:{note:"Select one color", qty:1}}, {start: {id:"A_Brakes"}, end: {id:"CG_Caliper"}, properties:{note:"Select calipers for front/rear", qty:2}}] AS row
MATCH (start:Assembly{id: row.start.id})
MATCH (end:ConfigGroup{id: row.end.id})
CREATE (start)-[r:REQUIRES]->(end) SET r += row.properties;
UNWIND [{start: {id:"MB1"}, end: {id:"P_Saddle"}, properties:{qty:1}}, {start: {id:"MB1"}, end: {id:"P_Pedals"}, properties:{qty:1}}] AS row
MATCH (start:Product{id: row.start.id})
MATCH (end:Part{id: row.end.id})
CREATE (start)-[r:HAS_PART]->(end) SET r += row.properties;
UNWIND [{start: {id:"CG_Gear_Sys"}, end: {id:"P_Gear_7"}, properties:{}}, {start: {id:"CG_Gear_Sys"}, end: {id:"P_Gear_11"}, properties:{}}, {start: {id:"CG_Gear_Sys"}, end: {id:"P_Gear_12"}, properties:{}}, {start: {id:"CG_Shifter"}, end: {id:"P_Shifter_Twist"}, properties:{}}, {start: {id:"CG_Shifter"}, end: {id:"P_Shifter_Trigger"}, properties:{}}, {start: {id:"CG_Derail"}, end: {id:"P_Derail_Basic"}, properties:{}}, {start: {id:"CG_Derail"}, end: {id:"P_Derail_Adv"}, properties:{}}, {start: {id:"CG_Caliper"}, end: {id:"P_Cal_Mech"}, properties:{}}, {start: {id:"CG_Caliper"}, end: {id:"P_Cal_Hyd"}, properties:{}}, {start: {id:"CG_Rim_26"}, end: {id:"P_Rim_Alloy_26"}, properties:{}}, {start: {id:"CG_Rim_26"}, end: {id:"P_Rim_Carbon_26"}, properties:{}}, {start: {id:"CG_Tire_26"}, end: {id:"P_Tire_Knobby_26"}, properties:{}}, {start: {id:"CG_Tire_26"}, end: {id:"P_Tire_Slick_26"}, properties:{}}, {start: {id:"CG_Rim_275"}, end: {id:"P_Rim_Alloy_275"}, properties:{}}, {start: {id:"CG_Rim_275"}, end: {id:"P_Rim_Carbon_275"}, properties:{}}, {start: {id:"CG_Tire_275"}, end: {id:"P_Tire_Knobby_275"}, properties:{}}, {start: {id:"CG_Tire_275"}, end: {id:"P_Tire_Slick_275"}, properties:{}}, {start: {id:"CG_Rim_29"}, end: {id:"P_Rim_Alloy_29"}, properties:{}}, {start: {id:"CG_Rim_29"}, end: {id:"P_Rim_Carbon_29"}, properties:{}}, {start: {id:"CG_Tire_29"}, end: {id:"P_Tire_Knobby_29"}, properties:{}}] AS row
MATCH (start:ConfigGroup{id: row.start.id})
MATCH (end:Part{id: row.end.id})
CREATE (start)-[r:HAS_OPTION]->(end) SET r += row.properties;
UNWIND [{start: {id:"CG_Tire_29"}, end: {id:"P_Tire_Slick_29"}, properties:{}}, {start: {id:"CG_Frame_Mat"}, end: {id:"P_Frame_Alum"}, properties:{}}, {start: {id:"CG_Frame_Mat"}, end: {id:"P_Frame_Carbon"}, properties:{}}, {start: {id:"CG_Frame_Mat"}, end: {id:"P_Frame_Steel"}, properties:{}}, {start: {id:"CG_Color"}, end: {id:"P_Color_Black"}, properties:{}}, {start: {id:"CG_Color"}, end: {id:"P_Color_Blue"}, properties:{}}, {start: {id:"CG_Color"}, end: {id:"P_Color_Green"}, properties:{}}] AS row
MATCH (start:ConfigGroup{id: row.start.id})
MATCH (end:Part{id: row.end.id})
CREATE (start)-[r:HAS_OPTION]->(end) SET r += row.properties;
UNWIND [{start: {id:"MB1"}, end: {id:"A_Drivetrain"}, properties:{qty:1}}, {start: {id:"MB1"}, end: {id:"A_Wheel"}, properties:{qty:2}}, {start: {id:"MB1"}, end: {id:"A_Frame"}, properties:{qty:1}}, {start: {id:"MB1"}, end: {id:"A_Brakes"}, properties:{qty:1}}] AS row
MATCH (start:Product{id: row.start.id})
MATCH (end:Assembly{id: row.end.id})
CREATE (start)-[r:REQUIRES]->(end) SET r += row.properties;
UNWIND [{start: {id:"MB1"}, end: {id:"CG_Gear_Sys"}, properties:{note:"Select one gear system", qty:1}}] AS row
MATCH (start:Product{id: row.start.id})
MATCH (end:ConfigGroup{id: row.end.id})
CREATE (start)-[r:REQUIRES]->(end) SET r += row.properties;
UNWIND [{start: {id:"CG_Wheel_Size"}, end: {id:"A_Wheel_26"}, properties:{}}, {start: {id:"CG_Wheel_Size"}, end: {id:"A_Wheel_275"}, properties:{}}, {start: {id:"CG_Wheel_Size"}, end: {id:"A_Wheel_29"}, properties:{}}] AS row
MATCH (start:ConfigGroup{id: row.start.id})
MATCH (end:Assembly{id: row.end.id})
CREATE (start)-[r:HAS_OPTION]->(end) SET r += row.properties;

5. Cypher 查询

这些 Cypher 查询兼容 Neo4j 2025.06+ 版本和 Cypher 25。

5.1. 获取变体

此查询通过根据约束创建解析链接来解析变体。它从产品到零件遍历 BOM,在每个 ConfigGroup 级别应用允许/禁止列表约束来过滤选项。由此产生的有效路径随后被用于创建 RESOLVED_LINK 关系,捕获指定变体所选的组件。

CYPHER 25

MATCH path = (p:Product)((x)-[r:!RESOLVED_LINK]->(y)
WHERE NOT (
    x:ConfigGroup
    AND any (cons IN $constraints WHERE (
      cons.category = x.category
      AND any (prop IN cons.properties WHERE
        (
          NOT prop.allow_list IS null
          AND NOT y[prop.name] IN prop.allow_list
        )
        OR (
          NOT prop.deny_list IS null
          AND y[prop.name] IN prop.deny_list
        )
        )
      )
    )
  )
)*(last:Part)
UNWIND relationships(path) AS rel
RETURN startNode(rel) AS source, rel AS r, endNode(rel) AS target

NEXT

MERGE (source)-[rl:RESOLVED_LINK {id_variant: $id_variant, rel_type: type(r)}]->(target)
SET rl.qty = r.qty

5.2. 显示解析后的 BOM

此查询可视化解析后的 BOM(现在可以运行,也可以在剪枝和清理步骤之后运行)

MATCH p=()-[:RESOLVED_LINK {id_variant: $id_variant}]->()
RETURN p
cbom unpruned loose bom
图 4. 未剪枝的宽松 BOM

5.3. 解析未决分支

此查询非常适合处理宽松约束,它使用评分机制自下而上递归剪除未决分支。它根据提供的评分策略参数评估选项:

  • 对于每个未决的 ConfigGroup,它计算每个选项分支的累计成本和重量。

  • 然后应用评分策略对选项进行排名。

    • 评分依赖于加权因素(例如成本、重量)。

    • 该策略由一对对的列表组成,每一对包含一个权重属性名称和一个乘数因子。

    • 负因子倾向于较小的值(例如对于成本或重量),

    • 而正因子倾向于较大的值(例如对于性能)。

    • 因子的绝对值反映了该字段在总分中的相对重要性。

    • 总分越高,选项越优。

    • 只保留得分最高的选项。

  • 最终,它保留得分最高的选项并删除其余选项。

CYPHER 25

// Converge before 10 iterations unless height of ConfigGroups tree is > 10
UNWIND range(1, 10) AS _
CALL (_) {

// Find unresolved ConfigGroups with no unresolved ConfigGroups underneath
MATCH (cf:ConfigGroup)
WHERE count{(cf)-[:RESOLVED_LINK {id_variant: $id_variant}]->()} > 1
AND NOT EXISTS {
  (cf)-[:RESOLVED_LINK {id_variant: $id_variant}]->+(cf_below:ConfigGroup)
  WHERE count{(cf_below)-[:RESOLVED_LINK {id_variant: $id_variant}]->()} > 1
}
RETURN cf

NEXT

// Compute costs and weight for all options of a given ConfigGroup
MATCH path = (leaf)<-[rs:RESOLVED_LINK {id_variant: $id_variant}]-*(opt)
    <-[opt_link:RESOLVED_LINK {id_variant: $id_variant}]-(cf)
WHERE NOT EXISTS {(leaf)-[:RESOLVED_LINK {id_variant: $id_variant}]->()}
WITH cf, opt_link, opt, leaf, reduce(acc=1,r IN rs | acc * coalesce(r.qty, 1)) AS times
WITH cf, opt_link, opt, leaf, {cost: leaf.cost * times, weight: leaf.weight * times} AS vals
WITH cf, opt_link, opt, collect(vals) AS vals_list
WITH cf, opt_link, opt, reduce (acc= {cost: 0.0, weight:0.0}, vals In vals_list|
    {cost: acc.cost + vals.cost, weight: acc.weight + vals.weight}) AS vals

// Apply scoring strategy set as parameter to select the best option
UNWIND $scoring AS field_factor
WITH cf, opt_link, opt, vals[field_factor.field] * field_factor.factor AS score
WITH cf, opt_link, opt, sum (score) AS score
ORDER BY cf, score DESC

// Detach unselected branch
WITH cf, collect(opt_link) AS opt_links
UNWIND opt_links[1..] AS del_opt_link
DELETE del_opt_link

}
cbom pruned loose bom
图 5. 剪枝后的宽松 BOM

5.4. 垃圾回收器

此查询清理在剪枝阶段与产品分离的解析链接(对于宽松约束很有用)

// Remove  RESOLVED_LINKs not connected to Product
MATCH ()<-[r:RESOLVED_LINK {id_variant: $id_variant}]-(x)
WHERE NOT EXISTS {(x)<-[:RESOLVED_LINK {id_variant: $id_variant}]-*(:Product)}
DELETE r
cbom clean loose bom
图 6. 剪枝且清理后的宽松 BOM

5.5. 计算重量和成本

此查询计算解析后 BOM 的总重量和总成本

// Compute weight and cost of resolved BOM
MATCH path = (leaf)<-[rs:RESOLVED_LINK {id_variant: $id_variant}]-*(:Product)
WHERE NOT EXISTS {(leaf)-[:RESOLVED_LINK {id_variant: $id_variant}]->()}
WITH leaf, reduce(acc=1,r IN rs | acc * coalesce(r.qty, 1)) AS times
WITH leaf, {cost: leaf.cost * times, weight: leaf.weight * times} AS vals
WITH collect(vals) AS vals_list
WITH reduce (acc= {cost: 0.0, weight:0.0}, vals In vals_list| {cost: acc.cost + vals.cost, weight: acc.weight + vals.weight}) AS vals
RETURN vals.weight AS weight, vals.cost AS cost

这是对剪枝且清理后的宽松 awesome_bike_loose_constraints 解析 BOM 的计算结果

{
    "weight": 8.43,
    "cost": 1045.0
}

5.6. 检查未满足的需求

此查询识别未决的配置组(对于过度约束的情况很有用)

// Find unresolved ConfigGroups
MATCH path = (:Product)-[rs:RESOLVED_LINK {id_variant: $id_variant}]-*(x)-[:REQUIRES]->(y:ConfigGroup)
WHERE NOT EXISTS {(x)-[:RESOLVED_LINK {id_variant: $id_variant}]->(y)}
RETURN path
cbom unresolved nocolor bom
图 7. 未决且过度约束的无颜色 BOM
© . This site is unofficial and not affiliated with Neo4j, Inc.