跳转到内容

功能特性#

Cypher 变更日志格式#

[所需插件版本 : 4.9.0.1]

该扩展支持 多语言 (polyglot) 变更日志(XML、SQL、YAML 等)。SQL 格式已被赋予更地道的 Cypher 格式别名。

Cypher 文件名必须以 .cypher 结尾。Cypher 变更日志必须以注释 --liquibase formatted cypher 开头。以下是一个受支持的 Cypher 文件示例

-- liquibase formatted cypher

-- changeset fbiville:my-movie-init
CREATE (:Movie {title: 'My Life', genre: 'Comedy'});

作为 文件夹包含 一部分的 Cypher 文件只能包含单个 Cypher 查询,且不能包含任何注释指令。

该扩展支持变更日志 文件文件夹 包含。

Cypher 和回滚变更#

[所需插件版本 (Cypher 别名) : 4.7.1.1]

支持内置的 SQL回滚 变更。SQL 变更也被别名为 cypher

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:neo4j="http://www.liquibase.org/xml/ns/dbchangelog-ext"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

    <changeSet id="my-movie-init" author="fbiville">
        <neo4j:cypher>CREATE (:Movie {title: 'My Life', genre: 'Comedy'})</neo4j:cypher>
    </changeSet>

    <changeSet id="translate" author="fbiville">
        <neo4j:cypher>MATCH (m:Movie {title: 'My Life'}) SET m.genre = 'Comédie'</neo4j:cypher>
        <rollback>MATCH (m:Movie {title: 'My Life'}) SET m.genre = 'Comedy'</rollback>
    </changeSet>

</databaseChangeLog>

警告

  • cypher XML 标签前需要添加相应的扩展命名空间前缀。
  • 如果查询包含 <> 等 XML 特殊字符,请确保将查询内容包裹在起始的 <![CDATA[ 和结束的 ]]> 中。
{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "my-movie-init",
        "author": "fbiville",
        "changes": [
          {
            "cypher": "CREATE (:Movie {title: 'My Life', genre: 'Comedy'})"
          }
        ]
      }
    },
    {
      "changeSet": {
        "id": "translate",
        "author": "fbiville",
        "changes": [
          {
            "cypher": "MATCH (m:Movie {title: 'My Life'}) SET m.genre = 'Comédie'"
          }
        ],
        "rollback": [
          {
            "cypher": "MATCH (m:Movie {title: 'My Life'}) SET m.genre = 'Comedy'"
          }
        ]
      }
    }
  ]
}
databaseChangeLog:
  - changeSet:
      id: my-movie-init
      author: fbiville
      changes:
        - cypher: 'CREATE (:Movie {title: ''My Life'', genre: ''Comedy''})'
  - changeSet:
      id: translate
      author: fbiville
      changes:
        - cypher: 'MATCH (m:Movie {title: ''My Life''}) SET m.genre = ''Comédie'''
      rollback:
        - cypher: 'MATCH (m:Movie {title: ''My Life''}) SET m.genre = ''Comedy'''
-- liquibase formatted cypher

-- changeset fbiville:my-movie-init
CREATE (:Movie {title: 'My Life', genre: 'Comedy'});

-- changeset fbiville:translate
MATCH (m:Movie {title: 'My Life'}) SET m.genre = 'Comédie'
-- rollback MATCH (m:Movie {title: 'My Life'}) SET m.genre = 'Comedy'

Neo4j 前置条件#

Liquibase 文档 中了解有关前置条件的更多信息,特别是 故障和错误处理部分

内置前置条件支持#

自插件发布以来,该扩展已在以下内置 前置条件 上进行了成功测试

  • dbms (针对 Neo4j)
  • sqlCheck (别名为 cypherCheck,见下文)

其他前置条件可能有效,但未经测试。

版本检查#

[所需插件版本 : 4.9.0]

version 前置条件断言运行时 Neo4j 版本指定的字符串开头。它可以与标准布尔运算符结合使用,与其他前置条件组合。

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:neo4j="http://www.liquibase.org/xml/ns/dbchangelog-ext"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

    <changeSet id="my-neo4j-44-deployment" author="fbiville">
        <preConditions onFail="CONTINUE">
            <neo4j:version matches="4.4"/>
        </preConditions>
        <neo4j:cypher>CREATE (:Neo4j {neo4j44: true})</neo4j:cypher>
    </changeSet>

    <changeSet id="my-neo4j-non44-deployment" author="fbiville">
        <preConditions onFail="CONTINUE">
            <not>
                <neo4j:version matches="4.4"/>
            </not>
        </preConditions>
        <neo4j:cypher>CREATE (:Neo4j {neo4j44: false})</neo4j:cypher>
    </changeSet>

</databaseChangeLog>
{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "my-neo4j-44-deployment",
        "author": "fbiville",
        "preConditions": [
          {
            "onFail": "CONTINUE"
          },
          {
            "version": {
              "matches": "4.4"
            }
          }
        ],
        "changes": [
          {
            "cypher": "CREATE (:Neo4j {neo4j44: true})"
          }
        ]
      }
    },
    {
      "changeSet": {
        "id": "my-neo4j-non44-deployment",
        "author": "fbiville",
        "preConditions": [
          {
            "onFail": "CONTINUE"
          },
          {
            "not": {
              "version": {
                "matches": "4.4"
              }
            }
          }
        ],
        "changes": [
          {
            "cypher": "CREATE (:Neo4j {neo4j44: false})"
          }
        ]
      }
    }
  ]
}
databaseChangeLog:
  - changeSet:
      id: my-neo4j-44-deployment
      author: fbiville
      preConditions:
        - onFail: 'CONTINUE'
        - version:
            matches: '4.4'
      changes:
        - cypher: 'CREATE (:Neo4j {neo4j44: true})'
  - changeSet:
      id: my-neo4j-non44-deployment
      author: fbiville
      preConditions:
        - onFail: 'CONTINUE'
        - not:
            - version:
                matches: '4.4'
      changes:
        - cypher: 'CREATE (:Neo4j {neo4j44: false})'

版本(Edition)检查#

[所需插件版本 : 4.9.0]

edition 检查断言目标 Neo4j 部署是社区版还是企业版。它可以与标准布尔运算符结合使用,与其他前置条件组合。

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:neo4j="http://www.liquibase.org/xml/ns/dbchangelog-ext"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

    <changeSet id="my-neo4j-ee-deployment" author="fbiville">
        <preConditions onFail="CONTINUE">
            <neo4j:edition enterprise="true"/>
        </preConditions>
        <neo4j:cypher>CREATE (:Neo4j {enterprise: true})</neo4j:cypher>
    </changeSet>

    <changeSet id="my-neo4j-ce-deployment" author="fbiville">
        <preConditions onFail="CONTINUE">
            <neo4j:edition enterprise="false"/>
        </preConditions>
        <neo4j:cypher>CREATE (:Neo4j {enterprise: false})</neo4j:cypher>
    </changeSet>

</databaseChangeLog>
{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "my-neo4j-ee-deployment",
        "author": "fbiville",
        "preConditions": [
          {
            "onFail": "CONTINUE"
          },
          {
            "edition": {
              "enterprise": true
            }
          }
        ],
        "changes": [
          {
            "cypher": "CREATE (:Neo4j {enterprise: true})"
          }
        ]
      }
    },
    {
      "changeSet": {
        "id": "my-neo4j-ce-deployment",
        "author": "fbiville",
        "preConditions": [
          {
            "onFail": "CONTINUE"
          },
          {
            "edition": {
              "enterprise": false
            }
          }
        ],
        "changes": [
          {
            "cypher": "CREATE (:Neo4j {enterprise: false})"
          }
        ]
      }
    }
  ]
}
databaseChangeLog:
  - changeSet:
      id: my-neo4j-ee-deployment
      author: fbiville
      preConditions:
        - edition:
            enterprise: true
        - onFail: 'CONTINUE'
      changes:
        - cypher: 'CREATE (:Neo4j {enterprise: true})'
  - changeSet:
      id: my-neo4j-ce-deployment
      author: fbiville
      preConditions:
        - edition:
            enterprise: false
        - onFail: 'CONTINUE'
      changes:
        - cypher: 'CREATE (:Neo4j {enterprise: false})'

Cypher 检查别名#

[所需插件版本 : 4.9.0]

cypherCheck 是现有 sqlCheck 前置条件的别名。目前,Cypher 格式的变更日志文件只能使用 sqlCheckcypherCheck 可以与标准布尔运算符结合使用,与其他前置条件组合。

警告

在 4.21.1.2 版本之前,JSON 和 YAML 变更日志必须指定 sql 属性而不是 cypher

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:neo4j="http://www.liquibase.org/xml/ns/dbchangelog-ext"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

    <changeSet id="my-neo4j-deployment" author="fbiville">
        <preConditions onFail="CONTINUE">
            <neo4j:cypherCheck expectedResult="0">MATCH (n:Neo4j) RETURN count(n)</neo4j:cypherCheck>
        </preConditions>
        <neo4j:cypher>CREATE (:Neo4j)</neo4j:cypher>
    </changeSet>

</databaseChangeLog>
{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "my-neo4j-deployment",
        "author": "fbiville",
        "preConditions": [
          {
            "onFail": "CONTINUE"
          },
          {
            "cypherCheck": {
              "expectedResult": "0",
              "cypher": "MATCH (n:Neo4j) RETURN count(n)"
            }
          }
        ],
        "changes": [
          {
            "cypher": {
              "cypher": "CREATE (:Neo4j)"
            }
          }
        ]
      }
    }
  ]
}
databaseChangeLog:
  - changeSet:
      id: my-neo4j-deployment
      author: fbiville
      preConditions:
        - onFail: 'CONTINUE'
        - cypherCheck:
            expectedResult: '0'
            cypher: MATCH (n:Neo4j) RETURN count(n)
      changes:
        - cypher:
            cypher: CREATE (:Neo4j)

插入变更#

[所需插件版本 : 4.21.1]

该变更允许定义单个节点的创建,并使用 labelName 指定一个标签。

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:neo4j="http://www.liquibase.org/xml/ns/dbchangelog-ext"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">

    <changeSet id="insert-node" author="fbiville">
        <neo4j:insert labelName="Person">
            <column name="id" value="8987212b-a6ff-48a1-901f-8c4b39bd6d9e" type="uuid"/>
            <column name="age" valueNumeric="30" type="integer"/>
            <column name="first_name" value="Florent"/>
            <column name="last_name" value="Biville"/>
            <column name="local_date" valueDate="2022-12-25" type="date"/>
            <column name="local_time" valueDate="22:23:24" type="date"/>
            <column name="local_date_time" valueDate="2018-02-01T12:13:14" type="date"/>
            <column name="zoned_date_time" valueDate="2020-07-12T22:23:24+02:00" type="date"/>
            <column name="polite" valueBoolean="true" type="boolean"/>
            <column name="picture" value="DLxmEfVUC9CAmjiNyVphWw==" type="blob"/>
            <column name="bio"
                    value="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean nisi tellus, elementum id mi vitae, faucibus lacinia purus. Integer nec velit sit amet velit tincidunt ultrices eu eu massa. Vestibulum in libero vel neque interdum blandit in non libero. Aenean iaculis, erat ac molestie laoreet, risus ex faucibus odio, a fermentum turpis elit eget ex. Donec volutpat bibendum enim pretium pulvinar. Proin rutrum neque dui, a suscipit tellus semper suscipit. Praesent lobortis ut lorem vitae volutpat. Pellentesque a lorem eu lacus faucibus facilisis nec sed metus. Aenean lacinia luctus ultricies. Pellentesque cursus justo non iaculis tristique. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Duis tempor nisi ut turpis bibendum facilisis. Donec aliquet porttitor lacus, non rhoncus lectus laoreet et."
                    type="clob"/>
        </neo4j:insert>
    </changeSet>
</databaseChangeLog>
{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "insert-node",
        "author": "fbiville",
        "changes": [
          {
            "insert": {
              "columns": [
                {
                  "column": {
                    "name": "id",
                    "type": "uuid",
                    "value": "8987212b-a6ff-48a1-901f-8c4b39bd6d9e"
                  }
                },
                {
                  "column": {
                    "name": "age",
                    "type": "integer",
                    "valueNumeric": 30
                  }
                },
                {
                  "column": {
                    "name": "first_name",
                    "value": "Florent"
                  }
                },
                {
                  "column": {
                    "name": "last_name",
                    "value": "Biville"
                  }
                },
                {
                  "column": {
                    "name": "local_date",
                    "type": "date",
                    "valueDate": "2022-12-25"
                  }
                },
                {
                  "column": {
                    "name": "local_time",
                    "type": "date",
                    "valueDate": "22:23:24"
                  }
                },
                {
                  "column": {
                    "name": "local_date_time",
                    "type": "date",
                    "valueDate": "2018-02-01T12:13:14"
                  }
                },
                {
                  "column": {
                    "name": "zoned_date_time",
                    "type": "date",
                    "valueDate": "2020-07-12T22:23:24+02:00"
                  }
                },
                {
                  "column": {
                    "name": "polite",
                    "type": "boolean",
                    "valueBoolean": true
                  }
                },
                {
                  "column": {
                    "name": "picture",
                    "type": "blob",
                    "value": "DLxmEfVUC9CAmjiNyVphWw=="
                  }
                },
                {
                  "column": {
                    "name": "bio",
                    "type": "clob",
                    "value": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean nisi tellus, elementum id mi vitae, faucibus lacinia purus. Integer nec velit sit amet velit tincidunt ultrices eu eu massa. Vestibulum in libero vel neque interdum blandit in non libero. Aenean iaculis, erat ac molestie laoreet, risus ex faucibus odio, a fermentum turpis elit eget ex. Donec volutpat bibendum enim pretium pulvinar. Proin rutrum neque dui, a suscipit tellus semper suscipit. Praesent lobortis ut lorem vitae volutpat. Pellentesque a lorem eu lacus faucibus facilisis nec sed metus. Aenean lacinia luctus ultricies. Pellentesque cursus justo non iaculis tristique. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Duis tempor nisi ut turpis bibendum facilisis. Donec aliquet porttitor lacus, non rhoncus lectus laoreet et."
                  }
                }
              ],
              "labelName": "Person"
            }
          }
        ]
      }
    }
  ]
}
databaseChangeLog:
  - changeSet:
      id: insert-node
      author: fbiville
      changes:
        - insert:
            columns:
              - column:
                  name: id
                  type: uuid
                  value: 8987212b-a6ff-48a1-901f-8c4b39bd6d9e
              - column:
                  name: age
                  type: integer
                  valueNumeric: !!float '30'
              - column:
                  name: first_name
                  value: Florent
              - column:
                  name: last_name
                  value: Biville
              - column:
                  name: local_date
                  type: date
                  valueDate: '2022-12-25'
              - column:
                  name: local_time
                  type: date
                  valueDate: '22:23:24'
              - column:
                  name: local_date_time
                  type: date
                  valueDate: '2018-02-01T12:13:14'
              - column:
                  name: zoned_date_time
                  type: date
                  valueDate: '2020-07-12T22:23:24+02:00'
              - column:
                  name: polite
                  type: boolean
                  valueBoolean: true
              - column:
                  name: picture
                  type: blob
                  value: DLxmEfVUC9CAmjiNyVphWw==
              - column:
                  name: bio
                  type: clob
                  value: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean
                    nisi tellus, elementum id mi vitae, faucibus lacinia purus. Integer
                    nec velit sit amet velit tincidunt ultrices eu eu massa. Vestibulum
                    in libero vel neque interdum blandit in non libero. Aenean iaculis,
                    erat ac molestie laoreet, risus ex faucibus odio, a fermentum turpis
                    elit eget ex. Donec volutpat bibendum enim pretium pulvinar. Proin rutrum
                    neque dui, a suscipit tellus semper suscipit. Praesent lobortis ut lorem
                    vitae volutpat. Pellentesque a lorem eu lacus faucibus facilisis nec
                    sed metus. Aenean lacinia luctus ultricies. Pellentesque cursus justo
                    non iaculis tristique. Vestibulum ante ipsum primis in faucibus orci
                    luctus et ultrices posuere cubilia curae; Duis tempor nisi ut turpis
                    bibendum facilisis. Donec aliquet porttitor lacus, non rhoncus lectus
                    laoreet et.
            labelName: Person

有关每一列支持的值类型,请参阅 加载数据 (Load Data) 文档。

加载数据#

[所需 Liquibase 核心版本 : 4.11.0] [所需插件版本 : 4.16.1.1]

假设有以下 (S)CSV data.scsv 文件

name;age;some_date;ignored;uuid;is_polite;blob
Florent;30.5;2022-12-25;ignored;8d1208fc-f401-496c-9cb8-483fef121234;false;DLxmEfVUC9CAmjiNyVphWw==
Andrea;32;2020-07-12T22:23:24+02:00;ignored!;1bc59ddb-8d4d-41d0-9c9a-34e837de5678;true;NULL
Nathan;34;2018-02-01T12:13:14;ignored!;123e4567-e89b-12d3-a456-426614174000;true;NULL
Robert;36;22:23:24;ignored!;9986a49a-0cce-4982-b491-b8177fd0ef81;true;NULL
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

    <changeSet id="customer-import" author="asanturbano">
        <loadData
                file="e2e/load-data/data.scsv"
                separator=";"
                tableName="CsvPerson">
            <column name="first_name" header="name" type="string"/>
            <column name="wisdom_index" header="age" type="numeric"/>
            <column name="some_date" index="2" type="date"/>
            <column name="_" header="ignored" type="skip"/>
            <column name="uuid" header="uuid" type="uuid"/>
            <column name="polite" header="is_polite" type="boolean"/>
            <column name="picture" header="blob" type="blob"/>
        </loadData>
    </changeSet>
</databaseChangeLog>
{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "customer-import",
        "author": "asanturbano",
        "changes": [
          {
            "loadData": {
              "columns": [
                {
                  "column": {
                    "header": "name",
                    "name": "first_name",
                    "type": "string"
                  }
                },
                {
                  "column": {
                    "header": "age",
                    "name": "wisdom_index",
                    "type": "numeric"
                  }
                },
                {
                  "column": {
                    "index": 2,
                    "name": "some_date",
                    "type": "date"
                  }
                },
                {
                  "column": {
                    "header": "ignored",
                    "name": "_",
                    "type": "skip"
                  }
                },
                {
                  "column": {
                    "header": "uuid",
                    "name": "uuid",
                    "type": "uuid"
                  }
                },
                {
                  "column": {
                    "header": "is_polite",
                    "name": "polite",
                    "type": "boolean"
                  }
                },
                {
                  "column": {
                    "header": "blob",
                    "name": "picture",
                    "type": "blob"
                  }
                }
              ],
              "file": "e2e/load-data/data.scsv",
              "separator": ";",
              "tableName": "CsvPerson"
            }
          }
        ]
      }
    }
  ]
}
databaseChangeLog:
  - changeSet:
      id: customer-import
      author: asanturbano
      changes:
        - loadData:
            columns:
              - column:
                  header: name
                  name: first_name
                  type: string
              - column:
                  header: age
                  name: wisdom_index
                  type: numeric
              - column:
                  index: 2
                  name: some_date
                  type: date
              - column:
                  header: ignored
                  name: _
                  type: skip
              - column:
                  header: uuid
                  name: uuid
                  type: uuid
              - column:
                  header: is_polite
                  name: polite
                  type: boolean
              - column:
                  header: blob
                  name: picture
                  type: blob
            file: e2e/load-data/data.scsv
            separator: ;
            tableName: CsvPerson

有关此变更的常规文档,请访问 此处

下表详细说明了每个支持的数据类型如何映射到其对应的 Neo4j 类型

加载数据类型 Liquibase Java 类型 示例值 结果 Neo4j Java 类型
BLOB 字符串 DLxmEfVUC9CAmjiNyVphWw== (base64 编码) byte[]
布尔值 (BOOLEAN) 布尔值 truefalse 布尔值
CLOB 字符串 字符串
DATE java.sql.Timestamp 2018-02-01T12:13:14 java.time.LocalDateTime
DATE java.sql.Date 2018-02-01 java.time.LocalDate
DATE java.sql.Time 12:13:14 java.time.LocalTime
DATE liquibase.statement.DatabaseFunction 2018-02-01T12:13:14+02:00 java.time.ZonedDateTime
NUMERIC liquibase.change.ColumnConfig.ValueNumeric 4242.0 LongDouble
STRING 字符串 "a string" 字符串
UUID 字符串 1bc59ddb-8d4d-41d0-9c9a-34e837de5678 字符串

也支持 SKIP:该值将被忽略。

警告

目前不支持 BLOB 文件(参见 issue)、CLOB 文件(参见 issue)、SEQUENCECOMPUTEDOTHERUNKNOWN 加载数据类型。

请确保使用正确的 valueXxx 属性

  • 布尔值使用 valueBoolean
  • 日期/时间值使用 valueDate
  • 数值使用 valueNumeric
  • 其他所有值使用 value

图重构#

节点合并#

[所需插件版本 : 4.13.0]

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:neo4j="http://www.liquibase.org/xml/ns/dbchangelog-ext"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

    <changeSet id="my-movie-init-oops" author="fbiville">
        <neo4j:cypher>CREATE (:Movie {title: 'My Life', genre: 'Comedy'})</neo4j:cypher>
        <neo4j:cypher>CREATE (:Movie {title: 'My Life', genre: 'Horror'})</neo4j:cypher>
        <neo4j:cypher>CREATE (:Movie {title: 'My Life', genre: 'Documentary'})</neo4j:cypher>
    </changeSet>

    <changeSet id="my-movie-init-fixed" author="fbiville">
        <neo4j:mergeNodes fragment="(m:Movie {title: 'My Life'}) WITH m ORDER BY m.genre ASC" outputVariable="m">
            <neo4j:propertyPolicy nameMatcher=".*" mergeStrategy="KEEP_FIRST"/>
        </neo4j:mergeNodes>
    </changeSet>

</databaseChangeLog>
{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "my-movie-init-oops",
        "author": "fbiville",
        "changes": [
          {
            "cypher": "CREATE (:Movie {title: 'My Life', genre: 'Comedy'})"
          },
          {
            "cypher": "CREATE (:Movie {title: 'My Life', genre: 'Horror'})"
          },
          {
            "cypher": "CREATE (:Movie {title: 'My Life', genre: 'Documentary'})"
          }
        ]
      }
    },
    {
      "changeSet": {
        "id": "my-movie-init-fixed",
        "author": "fbiville",
        "changes": [
          {
            "mergeNodes": {
              "fragment": "(m:Movie {title: 'My Life'}) WITH m ORDER BY m.genre ASC",
              "outputVariable": "m",
              "propertyPolicies": [
                {
                  "propertyPolicy": {
                    "mergeStrategy": "KEEP_FIRST",
                    "nameMatcher": ".*"
                  }
                }
              ]
            }
          }
        ]
      }
    }
  ]
}
databaseChangeLog:
  - changeSet:
      id: my-movie-init-oops
      author: fbiville
      changes:
        - cypher: 'CREATE (:Movie {title: ''My Life'', genre: ''Comedy''})'
        - cypher: 'CREATE (:Movie {title: ''My Life'', genre: ''Horror''})'
        - cypher: 'CREATE (:Movie {title: ''My Life'', genre: ''Documentary''})'
  - changeSet:
      id: my-movie-init-fixed
      author: fbiville
      changes:
        - mergeNodes:
            fragment: '(m:Movie {title: ''My Life''}) WITH m ORDER BY m.genre ASC'
            outputVariable: m
            propertyPolicies:
              - propertyPolicy:
                  mergeStrategy: 'KEEP_FIRST'
                  nameMatcher: .*

指定一个 Cypher 查询片段,用于定义要匹配以进行合并操作的节点。如果匹配到的节点少于两个,则合并操作不执行任何动作。

指定该片段中引用匹配节点的变量。这将被重用以创建要执行的内部合并 Cypher 查询。

最后,确保为每个持久化属性定义合并策略。扩展程序会遍历每个唯一的属性名称,并按声明顺序选择第一个匹配的合并策略。如果至少有一个属性名称没有匹配到策略,则合并失败并取消。一旦为属性名称匹配到了策略,将执行以下操作之一

  • KEEP_FIRST:保留该名称的第一个定义的属性值
  • KEEP_LAST:保留该名称的最后一个定义的属性值
  • KEEP_ALL:将所有定义的属性值聚合成一个数组(即使只找到单个值)

注意

"第一"和"最后"由指定的 Cypher 查询片段的顺序定义。强烈建议像示例中那样,使用 ORDER BY 子句显式对匹配到的节点进行排序。

节点属性提取#

[所需插件版本 : 4.17.2]

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:neo4j="http://www.liquibase.org/xml/ns/dbchangelog-ext"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

    <changeSet id="my-movie-init" author="fbiville">
        <neo4j:cypher>CREATE (:Movie {title: 'My Life', genre: 'Comedy'})</neo4j:cypher>
        <neo4j:cypher>CREATE (:Movie {title: 'My Project', genre: 'Comedy'})</neo4j:cypher>
    </changeSet>

    <changeSet id="genre-extraction" author="marouane">
        <neo4j:extractProperty property="genre" fromNodes="(m:Movie) WITH m ORDER BY id(m) ASC" nodesNamed="m">
            <neo4j:toNodes withLabel="Genre" withProperty="genre">
                <neo4j:linkedFromSource withType="HAS_GENRE" withDirection="OUTGOING" />
            </neo4j:toNodes>
        </neo4j:extractProperty>
    </changeSet>

</databaseChangeLog>
{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "my-movie-init",
        "author": "fbiville",
        "changes": [
          {
            "cypher": "CREATE (:Movie {title: 'My Life', genre: 'Comedy'})"
          },
          {
            "cypher": "CREATE (:Movie {title: 'My Project', genre: 'Comedy'})"
          }
        ]
      }
    },
    {
      "changeSet": {
        "id": "genre-extraction",
        "author": "marouane",
        "changes": [
          {
            "extractProperty": {
              "fromNodes": "(m:Movie) WITH m ORDER BY id(m) ASC",
              "nodesNamed": "m",
              "property": "genre",
              "toNodes": {
                "withLabel": "Genre",
                "withProperty": "genre",
                "linkedFromSource": {
                  "withDirection": "OUTGOING",
                  "withType": "HAS_GENRE"
                }
              }
            }
          }
        ]
      }
    }
  ]
}
databaseChangeLog:
  - changeSet:
      id: my-movie-init
      author: fbiville
      changes:
        - cypher: 'CREATE (:Movie {title: ''My Life'', genre: ''Comedy''})'
        - cypher: 'CREATE (:Movie {title: ''My Project'', genre: ''Comedy''})'
  - changeSet:
      id: genre-extraction
      author: marouane
      changes:
        - extractProperty:
            fromNodes: '(m:Movie) WITH m ORDER BY id(m) ASC'
            nodesNamed: 'm'
            property: 'genre'
            toNodes:
              withLabel: 'Genre'
              withProperty: 'genre'
              linkedFromSource:
                withDirection: 'OUTGOING'
                withType: 'HAS_GENRE'

节点属性提取重构允许将节点属性提取到其自己的节点中。与 节点合并重构 一样,要提取属性的节点由 Cypher 片段(fromNodes 属性)和绑定到这些节点的变量名(nodesNamed 属性)指定。要提取的属性名称由 property 属性指定。

由 Cypher 片段匹配到的源节点的属性将被删除。该属性将设置在提取出的节点上,名称由 withProperty 属性描述。提取节点的标签由 withLabel 属性定义。将 merge 属性设置为 true 可避免与可能存在相同标签和属性的节点发生重复。默认行为是每次都创建提取的节点。

可选地,提取的节点可以与源节点关联。在这种情况下,需要分别使用 withTypewithDirection 属性指定类型和方向。

注意

关系方向是从源节点的视角来看的。在示例中,OUTGOING 意味着关系从源节点开始并指向提取的节点。反之,INCOMING 意味着关系从提取的节点指向源节点。

也可以通过将相应的 merge 属性设置为 true 来避免关系重复。默认值是始终创建关系。

警告

在节点上设置 merge=false 而在关系上设置 merge=true 会触发验证警告。事实上,创建提取出的节点意味着也会创建新的关系。在这种情况下,在关系上设置 merge=true 会带来不必要的执行开销。

节点标签重命名#

[所需插件版本 : 4.25.0.1]

标签重命名重构允许将一个标签重命名为另一个,匹配其全部或部分节点,可在单个事务中或分批执行。

如下图所示,重构的主要属性包括

  • from:现有标签的值
  • to:新标签的值,替换现有标签

全局重命名#

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:neo4j="http://www.liquibase.org/xml/ns/dbchangelog-ext"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

    <changeSet id="my-movie-init-oops" author="fbiville">
        <neo4j:cypher>CREATE (:Movie {title: 'My Life', genre: 'Comedy'})</neo4j:cypher>
        <neo4j:cypher>CREATE (:Book {title: 'My Life', genre: 'Autobiography'})</neo4j:cypher>
    </changeSet>

    <changeSet id="my-movie-init-fixed" author="fbiville">
        <neo4j:renameLabel from="Movie" to="Film" />
    </changeSet>

</databaseChangeLog>
{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "my-movie-init-oops",
        "author": "fbiville",
        "changes": [
          {
            "cypher": "CREATE (:Movie {title: 'My Life', genre: 'Comedy'})"
          },
          {
            "cypher": "CREATE (:Book {title: 'My Life', genre: 'Autobiography'})"
          }
        ]
      }
    },
    {
      "changeSet": {
        "id": "my-movie-init-fixed",
        "author": "fbiville",
        "changes": [
          {
            "renameLabel": {
              "from": "Movie",
              "to": "Film"
            }
          }
        ]
      }
    }
  ]
}
databaseChangeLog:
  - changeSet:
      id: my-movie-init-oops
      author: fbiville
      changes:
        - cypher: 'CREATE (:Movie {title: ''My Life'', genre: ''Comedy''})'
        - cypher: 'CREATE (:Book {title: ''My Life'', genre: ''Autobiography''})'
  - changeSet:
      id: my-movie-init-fixed
      author: fbiville
      changes:
        - renameLabel:
            from: 'Movie'
            to: 'Film'

由于此操作可能会影响大量数据,在单个事务中运行该变更可能不可行,因为事务运行速度可能太慢,甚至可能导致内存溢出。

为了防止这种情况,必须将 enableBatchImport 设置为 true。由于它底层依赖于 CALL {} IN TRANSACTIONS,因此所包含 Change Set 的 runInTransaction 也必须设置为 false。这导致变更以批次方式执行。

警告

此设置仅在目标 Neo4j 实例支持 CALL {} IN TRANSACTIONS(4.4 及更高版本)时有效。如果不支持,Neo4j 插件将在单个自动提交事务中运行该变更。

请务必阅读有关 更改 runInTransaction 的后果

batchSize 属性控制运行多少个事务。如果未设置该属性,则批次大小由 Neo4j 服务器端定义。

Neo4j 5.7 中引入的 内部事务的错误策略 可通过 batchErrorPolicy 属性进行配置,支持的值为:- CONTINUE - BREAK - FAIL

自 Neo4j 5.21 起,内部事务还可以配置为 并行运行。布尔值 concurrent 属性控制该行为。并发默认禁用。

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:neo4j="http://www.liquibase.org/xml/ns/dbchangelog-ext"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

    <changeSet id="my-movie-init-oops" author="fbiville">
        <neo4j:cypher>CREATE (:Movie {title: 'My Life', genre: 'Comedy'})</neo4j:cypher>
        <neo4j:cypher>CREATE (:Book {title: 'My Life', genre: 'Autobiography'})</neo4j:cypher>
    </changeSet>

    <changeSet id="my-movie-init-fixed" author="fbiville" runInTransaction="false">
        <neo4j:renameLabel from="Movie" to="Film" enableBatchImport="true" batchSize="1" />
    </changeSet>

</databaseChangeLog>
{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "my-movie-init-oops",
        "author": "fbiville",
        "changes": [
          {
            "cypher": "CREATE (:Movie {title: 'My Life', genre: 'Comedy'})"
          },
          {
            "cypher": "CREATE (:Book {title: 'My Life', genre: 'Autobiography'})"
          }
        ]
      }
    },
    {
      "changeSet": {
        "id": "my-movie-init-fixed",
        "author": "fbiville",
        "runInTransaction": false,
        "changes": [
          {
            "renameLabel": {
              "from": "Movie",
              "to": "Film",
              "enableBatchImport": true,
              "batchSize": 1
            }
          }
        ]
      }
    }
  ]
}
databaseChangeLog:
  - changeSet:
      id: my-movie-init-oops
      author: fbiville
      changes:
        - cypher: 'CREATE (:Movie {title: ''My Life'', genre: ''Comedy''})'
        - cypher: 'CREATE (:Book {title: ''My Life'', genre: ''Autobiography''})'
  - changeSet:
      id: my-movie-init-fixed
      author: fbiville
      runInTransaction: false
      changes:
        - renameLabel:
            from: 'Movie'
            to: 'Film'
            enableBatchImport: true
            batchSize: 1

局部重命名#

也可以设置以下属性,以仅匹配带有 from 指定标签的节点的子集

  • fragment 指定匹配节点的模式
  • outputVariable 指定在 fragment 中定义并表示目标节点的 Cypher 变量名

注意

将要重命名的节点位于 fragment 中定义的节点与标签由 from 指定的节点的交集处。换句话说,如果 fragment 中定义的节点都不携带 from 定义的标签,则重命名不会修改任何这些节点。

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:neo4j="http://www.liquibase.org/xml/ns/dbchangelog-ext"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

    <changeSet id="my-movie-init-oops" author="fbiville">
        <neo4j:cypher>CREATE (:Movie {title: 'My Life', genre: 'Comedy'})</neo4j:cypher>
        <neo4j:cypher>CREATE (:Movie {title: 'My Birthday', genre: 'Musical'})</neo4j:cypher>
    </changeSet>

    <changeSet id="my-movie-init-fixed" author="fbiville">
        <neo4j:renameLabel from="Movie" to="Film" fragment="(m:Movie {title: 'My Birthday'})" outputVariable="m"  />
    </changeSet>

</databaseChangeLog>
{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "my-movie-init-oops",
        "author": "fbiville",
        "changes": [
          {
            "cypher": "CREATE (:Movie {title: 'My Life', genre: 'Comedy'})"
          },
          {
            "cypher": "CREATE (:Movie {title: 'My Birthday', genre: 'Musical'})"
          }
        ]
      }
    },
    {
      "changeSet": {
        "id": "my-movie-init-fixed",
        "author": "fbiville",
        "changes": [
          {
            "renameLabel": {
              "from": "Movie",
              "to": "Film",
              "fragment": "(m:Movie {title: 'My Birthday'})",
              "outputVariable": "m"
            }
          }
        ]
      }
    }
  ]
}
databaseChangeLog:
  - changeSet:
      id: my-movie-init-oops
      author: fbiville
      changes:
        - cypher: 'CREATE (:Movie {title: ''My Life'', genre: ''Comedy''})'
        - cypher: 'CREATE (:Movie {title: ''My Birthday'', genre: ''Musical''})'
  - changeSet:
      id: my-movie-init-fixed
      author: fbiville
      changes:
        - renameLabel:
            from: 'Movie'
            to: 'Film'
            fragment: '(m:Movie {title: ''My Birthday''})'
            outputVariable: 'm'

由于此操作可能会影响大量节点,在单个事务中运行该变更可能不可行,因为事务运行速度可能太慢,甚至可能导致内存溢出。

由于此操作可能会影响大量数据,在单个事务中运行该变更可能不可行,因为事务运行速度可能太慢,甚至可能导致内存溢出。

为了防止这种情况,必须将 enableBatchImport 设置为 true。由于它底层依赖于 CALL {} IN TRANSACTIONS,因此所包含 Change Set 的 runInTransaction 也必须设置为 false。这导致变更以批次方式执行。

警告

此设置仅在目标 Neo4j 实例支持 CALL {} IN TRANSACTIONS(4.4 及更高版本)时有效。如果不支持,Neo4j 插件将在单个自动提交事务中运行该变更。

请务必阅读有关 更改 runInTransaction 的后果

batchSize 属性控制运行多少个事务。如果未设置该属性,则批次大小由 Neo4j 服务器端定义。

Neo4j 5.7 中引入的 内部事务的错误策略 可通过 batchErrorPolicy 属性进行配置,支持的值为:- CONTINUE - BREAK - FAIL

自 Neo4j 5.21 起,内部事务还可以配置为 并行运行。布尔值 concurrent 属性控制该行为。并发默认禁用。

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:neo4j="http://www.liquibase.org/xml/ns/dbchangelog-ext"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

    <changeSet id="my-movie-init-oops" author="fbiville">
        <neo4j:cypher>CREATE (:Movie {title: 'My Life', genre: 'Comedy'})</neo4j:cypher>
        <neo4j:cypher>CREATE (:Movie {title: 'My Birthday', genre: 'Musical'})</neo4j:cypher>
    </changeSet>

    <changeSet id="my-movie-init-fixed" author="fbiville" runInTransaction="false">
        <neo4j:renameLabel from="Movie" to="Film" fragment="(m:Movie {title: 'My Birthday'})" outputVariable="m" enableBatchImport="true" batchSize="1" />
    </changeSet>

</databaseChangeLog>
{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "my-movie-init-oops",
        "author": "fbiville",
        "changes": [
          {
            "cypher": "CREATE (:Movie {title: 'My Life', genre: 'Comedy'})"
          },
          {
            "cypher": "CREATE (:Movie {title: 'My Birthday', genre: 'Musical'})"
          }
        ]
      }
    },
    {
      "changeSet": {
        "id": "my-movie-init-fixed",
        "author": "fbiville",
        "runInTransaction": false,
        "changes": [
          {
            "renameLabel": {
              "from": "Movie",
              "to": "Film",
              "fragment": "(m:Movie {title: 'My Birthday'})",
              "outputVariable": "m",
              "enableBatchImport": true,
              "batchSize": 1
            }
          }
        ]
      }
    }
  ]
}
databaseChangeLog:
  - changeSet:
      id: my-movie-init-oops
      author: fbiville
      changes:
        - cypher: 'CREATE (:Movie {title: ''My Life'', genre: ''Comedy''})'
        - cypher: 'CREATE (:Movie {title: ''My Birthday'', genre: ''Musical''})'
  - changeSet:
      id: my-movie-init-fixed
      author: fbiville
      runInTransaction: false
      changes:
        - renameLabel:
            from: 'Movie'
            to: 'Film'
            fragment: '(m:Movie {title: ''My Birthday''})'
            outputVariable: 'm'
            enableBatchImport: true
            batchSize: 1

关系类型重命名#

[所需插件版本 : 4.25.0.1]

类型重命名重构允许将一种类型重命名为另一种,匹配其全部或部分关系,可在单个事务中或分批执行。

如下图所示,重构的主要属性包括

  • from:现有类型的值
  • to:新类型的值,替换现有类型

全局重命名#

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:neo4j="http://www.liquibase.org/xml/ns/dbchangelog-ext"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

    <changeSet id="my-movie-init-oops" author="fbiville">
        <neo4j:cypher><![CDATA[CREATE (:Movie)-[:SEEN_BY {date: 'now'}]->(:Person)]]></neo4j:cypher>
        <neo4j:cypher><![CDATA[CREATE (:Movie)-[:SEEN_BY {date: 'yesterday'}]->(:Dog)]]></neo4j:cypher>
    </changeSet>

    <changeSet id="my-movie-init-fixed" author="fbiville">
        <neo4j:renameType from="SEEN_BY" to="VIEWED_BY" />
    </changeSet>

</databaseChangeLog>
{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "my-movie-init-oops",
        "author": "fbiville",
        "changes": [
          {
            "cypher": "CREATE (:Movie)-[:SEEN_BY {date: 'now'}]->(:Person)"
          },
          {
            "cypher": "CREATE (:Movie)-[:SEEN_BY {date: 'yesterday'}]->(:Dog)"
          }
        ]
      }
    },
    {
      "changeSet": {
        "id": "my-movie-init-fixed",
        "author": "fbiville",
        "changes": [
          {
            "renameType": {
              "from": "SEEN_BY",
              "to": "VIEWED_BY"
            }
          }
        ]
      }
    }
  ]
}
databaseChangeLog:
  - changeSet:
      id: my-movie-init-oops
      author: fbiville
      changes:
        - cypher: 'CREATE (:Movie)-[:SEEN_BY {date: ''now''}]->(:Person)'
        - cypher: 'CREATE (:Movie)-[:SEEN_BY {date: ''yesterday''}]->(:Dog)'
  - changeSet:
      id: my-movie-init-fixed
      author: fbiville
      changes:
        - renameType:
            from: 'SEEN_BY'
            to: 'VIEWED_BY'

由于此操作可能会影响大量数据,在单个事务中运行该变更可能不可行,因为事务运行速度可能太慢,甚至可能导致内存溢出。

为了防止这种情况,必须将 enableBatchImport 设置为 true。由于它底层依赖于 CALL {} IN TRANSACTIONS,因此所包含 Change Set 的 runInTransaction 也必须设置为 false。这导致变更以批次方式执行。

警告

此设置仅在目标 Neo4j 实例支持 CALL {} IN TRANSACTIONS(4.4 及更高版本)时有效。如果不支持,Neo4j 插件将在单个自动提交事务中运行该变更。

请务必阅读有关 更改 runInTransaction 的后果

batchSize 属性控制运行多少个事务。如果未设置该属性,则批次大小由 Neo4j 服务器端定义。

Neo4j 5.7 中引入的 内部事务的错误策略 可通过 batchErrorPolicy 属性进行配置,支持的值为:- CONTINUE - BREAK - FAIL

自 Neo4j 5.21 起,内部事务还可以配置为 并行运行。布尔值 concurrent 属性控制该行为。并发默认禁用。

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:neo4j="http://www.liquibase.org/xml/ns/dbchangelog-ext"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

    <changeSet id="my-movie-init-oops" author="fbiville">
        <neo4j:cypher><![CDATA[CREATE (:Movie)-[:SEEN_BY {date: 'now'}]->(:Person)]]></neo4j:cypher>
        <neo4j:cypher><![CDATA[CREATE (:Movie)-[:SEEN_BY {date: 'yesterday'}]->(:Dog)]]></neo4j:cypher>
    </changeSet>

    <changeSet id="my-movie-init-fixed" author="fbiville" runInTransaction="false">
        <neo4j:renameType from="SEEN_BY" to="VIEWED_BY" enableBatchImport="true" batchSize="1" />
    </changeSet>

</databaseChangeLog>
{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "my-movie-init-oops",
        "author": "fbiville",
        "changes": [
          {
            "cypher": "CREATE (:Movie)-[:SEEN_BY {date: 'now'}]->(:Person)"
          },
          {
            "cypher": "CREATE (:Movie)-[:SEEN_BY {date: 'yesterday'}]->(:Dog)"
          }
        ]
      }
    },
    {
      "changeSet": {
        "id": "my-movie-init-fixed",
        "author": "fbiville",
        "runInTransaction": false,
        "changes": [
          {
            "renameType": {
              "from": "SEEN_BY",
              "to": "VIEWED_BY",
              "enableBatchImport": true,
              "batchSize": 1
            }
          }
        ]
      }
    }
  ]
}
databaseChangeLog:
  - changeSet:
      id: my-movie-init-oops
      author: fbiville
      changes:
        - cypher: 'CREATE (:Movie)-[:SEEN_BY {date: ''now''}]->(:Person)'
        - cypher: 'CREATE (:Movie)-[:SEEN_BY {date: ''yesterday''}]->(:Dog)'
  - changeSet:
      id: my-movie-init-fixed
      author: fbiville
      runInTransaction: false
      changes:
        - renameType:
            from: 'SEEN_BY'
            to: 'VIEWED_BY'
            enableBatchImport: true
            batchSize: 1

局部重命名#

也可以设置以下属性,以仅匹配带有 from 指定类型的关系的子集

  • fragment 指定匹配关系的模式
  • outputVariable 指定在 fragment 中定义并表示目标关系的 Cypher 变量名

注意

将要重命名的关系位于 fragment 中定义的关系与类型由 from 指定的关系的交集处。换句话说,如果 fragment 中定义的关系都不具有 from 定义的类型,则重命名不会修改任何这些关系。

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:neo4j="http://www.liquibase.org/xml/ns/dbchangelog-ext"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

    <changeSet id="my-movie-init-oops" author="fbiville">
        <neo4j:cypher><![CDATA[CREATE (:Movie)-[:SEEN_BY {date: 'now'}]->(:Person)]]></neo4j:cypher>
        <neo4j:cypher><![CDATA[CREATE (:Movie)-[:SEEN_BY {date: 'yesterday'}]->(:Dog)]]></neo4j:cypher>
    </changeSet>

    <changeSet id="my-movie-init-fixed" author="fbiville">
        <neo4j:renameType from="SEEN_BY" to="VIEWED_BY" fragment="(:Person)&lt;-[r:SEEN_BY]-()" outputVariable="r"  />
    </changeSet>

</databaseChangeLog>
{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "my-movie-init-oops",
        "author": "fbiville",
        "changes": [
          {
            "cypher": "CREATE (:Movie)-[:SEEN_BY {date: 'now'}]->(:Person)"
          },
          {
            "cypher": "CREATE (:Movie)-[:SEEN_BY {date: 'yesterday'}]->(:Dog)"
          }
        ]
      }
    },
    {
      "changeSet": {
        "id": "my-movie-init-fixed",
        "author": "fbiville",
        "changes": [
          {
            "renameType": {
              "from": "SEEN_BY",
              "to": "VIEWED_BY",
              "fragment": "(:Person)<-[r:SEEN_BY]-()",
              "outputVariable": "r"
            }
          }
        ]
      }
    }
  ]
}
databaseChangeLog:
  - changeSet:
      id: my-movie-init-oops
      author: fbiville
      changes:
        - cypher: 'CREATE (:Movie)-[:SEEN_BY {date: ''now''}]->(:Person)'
        - cypher: 'CREATE (:Movie)-[:SEEN_BY {date: ''yesterday''}]->(:Dog)'
  - changeSet:
      id: my-movie-init-fixed
      author: fbiville
      changes:
        - renameType:
            from: 'SEEN_BY'
            to: 'VIEWED_BY'
            fragment: '(:Person)<-[r:SEEN_BY]-()'
            outputVariable: 'r'

由于此操作可能会影响大量数据,在单个事务中运行该变更可能不可行,因为事务运行速度可能太慢,甚至可能导致内存溢出。

为了防止这种情况,必须将 enableBatchImport 设置为 true。由于它底层依赖于 CALL {} IN TRANSACTIONS,因此所包含 Change Set 的 runInTransaction 也必须设置为 false。这导致变更以批次方式执行。

警告

此设置仅在目标 Neo4j 实例支持 CALL {} IN TRANSACTIONS(4.4 及更高版本)时有效。如果不支持,Neo4j 插件将在单个自动提交事务中运行该变更。

请务必阅读有关 更改 runInTransaction 的后果

batchSize 属性控制运行多少个事务。如果未设置该属性,则批次大小由 Neo4j 服务器端定义。

Neo4j 5.7 中引入的 内部事务的错误策略 可通过 batchErrorPolicy 属性进行配置,支持的值为:- CONTINUE - BREAK - FAIL

自 Neo4j 5.21 起,内部事务还可以配置为 并行运行。布尔值 concurrent 属性控制该行为。并发默认禁用。

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:neo4j="http://www.liquibase.org/xml/ns/dbchangelog-ext"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

    <changeSet id="my-movie-init-oops" author="fbiville">
        <neo4j:cypher><![CDATA[CREATE (:Movie)-[:SEEN_BY {date: 'now'}]->(:Person)]]></neo4j:cypher>
        <neo4j:cypher><![CDATA[CREATE (:Movie)-[:SEEN_BY {date: 'yesterday'}]->(:Dog)]]></neo4j:cypher>
    </changeSet>

    <changeSet id="my-movie-init-fixed" author="fbiville" runInTransaction="false">
        <neo4j:renameType from="SEEN_BY" to="VIEWED_BY" fragment="(:Person)&lt;-[r:SEEN_BY]-()" outputVariable="r" enableBatchImport="true" batchSize="1" />
    </changeSet>

</databaseChangeLog>
{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "my-movie-init-oops",
        "author": "fbiville",
        "changes": [
          {
            "cypher": "CREATE (:Movie)-[:SEEN_BY {date: 'now'}]->(:Person)"
          },
          {
            "cypher": "CREATE (:Movie)-[:SEEN_BY {date: 'yesterday'}]->(:Dog)"
          }
        ]
      }
    },
    {
      "changeSet": {
        "id": "my-movie-init-fixed",
        "author": "fbiville",
        "runInTransaction": false,
        "changes": [
          {
            "renameType": {
              "from": "SEEN_BY",
              "to": "VIEWED_BY",
              "fragment": "(:Person)<-[r:SEEN_BY]-()",
              "outputVariable": "r",
              "enableBatchImport": true,
              "batchSize": 1
            }
          }
        ]
      }
    }
  ]
}
databaseChangeLog:
  - changeSet:
      id: my-movie-init-oops
      author: fbiville
      changes:
        - cypher: 'CREATE (:Movie)-[:SEEN_BY {date: ''now''}]->(:Person)'
        - cypher: 'CREATE (:Movie)-[:SEEN_BY {date: ''yesterday''}]->(:Dog)'
  - changeSet:
      id: my-movie-init-fixed
      author: fbiville
      runInTransaction: false
      changes:
        - renameType:
            from: 'SEEN_BY'
            to: 'VIEWED_BY'
            fragment: '(:Person)<-[r:SEEN_BY]-()'
            outputVariable: 'r'
            enableBatchImport: true
            batchSize: 1

关系方向反转#

[所需插件版本 : 4.25.1.1]

方向反转重构允许翻转指定类型关系的起点和终点节点,匹配全部或部分关系,可在单个事务中或分批执行。

如下图所示,重构的主要属性包括

  • type:要反转的关系类型

全局反转#

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:neo4j="http://www.liquibase.org/xml/ns/dbchangelog-ext"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

    <changeSet id="my-movie-init-oops" author="fbiville">
        <neo4j:cypher><![CDATA[CREATE (:Movie)<-[:VIEWED_BY {date: 'now'}]-(:Person)]]></neo4j:cypher>
        <neo4j:cypher><![CDATA[CREATE (:Movie)<-[:VIEWED_BY {date: 'yesterday'}]-(:Dog)]]></neo4j:cypher>
    </changeSet>

    <changeSet id="my-movie-init-fixed" author="fbiville">
        <neo4j:invertDirection type="VIEWED_BY" />
    </changeSet>

</databaseChangeLog>
{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "my-movie-init-oops",
        "author": "fbiville",
        "changes": [
          {
            "cypher": "CREATE (:Movie)<-[:VIEWED_BY {date: 'now'}]-(:Person)"
          },
          {
            "cypher": "CREATE (:Movie)<-[:VIEWED_BY {date: 'yesterday'}]-(:Dog)"
          }
        ]
      }
    },
    {
      "changeSet": {
        "id": "my-movie-init-fixed",
        "author": "fbiville",
        "changes": [
          {
            "invertDirection": {
              "type": "VIEWED_BY"
            }
          }
        ]
      }
    }
  ]
}
databaseChangeLog:
  - changeSet:
      id: my-movie-init-oops
      author: fbiville
      changes:
        - cypher: 'CREATE (:Movie)<-[:VIEWED_BY {date: ''now''}]-(:Person)'
        - cypher: 'CREATE (:Movie)<-[:VIEWED_BY {date: ''yesterday''}]-(:Dog)'
  - changeSet:
      id: my-movie-init-fixed
      author: fbiville
      changes:
        - invertDirection:
            type: 'VIEWED_BY'

由于此操作可能会影响大量数据,在单个事务中运行该变更可能不可行,因为事务运行速度可能太慢,甚至可能导致内存溢出。

为了防止这种情况,必须将 enableBatchImport 设置为 true。由于它底层依赖于 CALL {} IN TRANSACTIONS,因此所包含 Change Set 的 runInTransaction 也必须设置为 false。这导致变更以批次方式执行。

警告

此设置仅在目标 Neo4j 实例支持 CALL {} IN TRANSACTIONS(4.4 及更高版本)时有效。如果不支持,Neo4j 插件将在单个自动提交事务中运行该变更。

请务必阅读有关 更改 runInTransaction 的后果

batchSize 属性控制运行多少个事务。如果未设置该属性,则批次大小由 Neo4j 服务器端定义。

Neo4j 5.7 中引入的 内部事务的错误策略 可通过 batchErrorPolicy 属性进行配置,支持的值为:- CONTINUE - BREAK - FAIL

自 Neo4j 5.21 起,内部事务还可以配置为 并行运行。布尔值 concurrent 属性控制该行为。并发默认禁用。

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:neo4j="http://www.liquibase.org/xml/ns/dbchangelog-ext"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

    <changeSet id="my-movie-init-oops" author="fbiville">
        <neo4j:cypher><![CDATA[CREATE (:Movie)<-[:VIEWED_BY {date: 'now'}]-(:Person)]]></neo4j:cypher>
        <neo4j:cypher><![CDATA[CREATE (:Movie)<-[:VIEWED_BY {date: 'yesterday'}]-(:Dog)]]></neo4j:cypher>
    </changeSet>

    <changeSet id="my-movie-init-fixed" author="fbiville" runInTransaction="false">
        <neo4j:invertDirection type="VIEWED_BY" enableBatchImport="true" batchSize="1" />
    </changeSet>

</databaseChangeLog>
{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "my-movie-init-oops",
        "author": "fbiville",
        "changes": [
          {
            "cypher": "CREATE (:Movie)<-[:VIEWED_BY {date: 'now'}]-(:Person)"
          },
          {
            "cypher": "CREATE (:Movie)<-[:VIEWED_BY {date: 'yesterday'}]-(:Dog)"
          }
        ]
      }
    },
    {
      "changeSet": {
        "id": "my-movie-init-fixed",
        "author": "fbiville",
        "runInTransaction": false,
        "changes": [
          {
            "invertDirection": {
              "type": "VIEWED_BY",
              "enableBatchImport": true,
              "batchSize": 1
            }
          }
        ]
      }
    }
  ]
}
databaseChangeLog:
  - changeSet:
      id: my-movie-init-oops
      author: fbiville
      changes:
        - cypher: 'CREATE (:Movie)<-[:VIEWED_BY {date: ''now''}]-(:Person)'
        - cypher: 'CREATE (:Movie)<-[:VIEWED_BY {date: ''yesterday''}]-(:Dog)'
  - changeSet:
      id: my-movie-init-fixed
      author: fbiville
      runInTransaction: false
      changes:
        - invertDirection:
            type: 'VIEWED_BY'
            enableBatchImport: true
            batchSize: 1

局部反转#

也可以设置以下属性,以仅匹配带有 type 指定类型的关系的子集

  • fragment 指定匹配关系的模式
  • outputVariable 指定在 fragment 中定义并表示目标关系的 Cypher 变量名

注意

将要反转的关系位于 fragment 中定义的关系与类型由 type 指定的关系的交集处。换句话说,如果 fragment 中定义的关系都不具有 type 定义的类型,则反转不会修改任何这些关系。

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:neo4j="http://www.liquibase.org/xml/ns/dbchangelog-ext"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

    <changeSet id="my-movie-init-oops" author="fbiville">
        <neo4j:cypher><![CDATA[CREATE (:Movie)-[:VIEWED_BY {date: 'now'}]->(:Person)]]></neo4j:cypher>
        <neo4j:cypher><![CDATA[CREATE (:Movie)-[:VIEWED_BY {date: 'yesterday'}]->(:Dog)]]></neo4j:cypher>
    </changeSet>

    <changeSet id="my-movie-init-fixed" author="fbiville">
        <neo4j:invertDirection type="VIEWED_BY" fragment="(:Person)-[r:VIEWED_BY]-&gt;()" outputVariable="r"  />
    </changeSet>

</databaseChangeLog>
{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "my-movie-init-oops",
        "author": "fbiville",
        "changes": [
          {
            "cypher": "CREATE (:Movie)<-[:VIEWED_BY {date: 'now'}]-(:Person)"
          },
          {
            "cypher": "CREATE (:Movie)-[:VIEWED_BY {date: 'yesterday'}]->(:Dog)"
          }
        ]
      }
    },
    {
      "changeSet": {
        "id": "my-movie-init-fixed",
        "author": "fbiville",
        "changes": [
          {
            "invertDirection": {
              "type": "VIEWED_BY",
              "fragment": "(:Person)-[r:VIEWED_BY]->()",
              "outputVariable": "r"
            }
          }
        ]
      }
    }
  ]
}
databaseChangeLog:
  - changeSet:
      id: my-movie-init-oops
      author: fbiville
      changes:
        - cypher: 'CREATE (:Movie)<-[:VIEWED_BY {date: ''now''}]-(:Person)'
        - cypher: 'CREATE (:Movie)-[:VIEWED_BY {date: ''yesterday''}]->(:Dog)'
  - changeSet:
      id: my-movie-init-fixed
      author: fbiville
      changes:
        - invertDirection:
            type: 'VIEWED_BY'
            fragment: '(:Person)-[r:VIEWED_BY]->()'
            outputVariable: 'r'

由于此操作可能会影响大量数据,在单个事务中运行该变更可能不可行,因为事务运行速度可能太慢,甚至可能导致内存溢出。

为了防止这种情况,必须将 enableBatchImport 设置为 true。由于它底层依赖于 CALL {} IN TRANSACTIONS,因此所包含 Change Set 的 runInTransaction 也必须设置为 false。这导致变更以批次方式执行。

警告

此设置仅在目标 Neo4j 实例支持 CALL {} IN TRANSACTIONS(4.4 及更高版本)时有效。如果不支持,Neo4j 插件将在单个自动提交事务中运行该变更。

请务必阅读有关 更改 runInTransaction 的后果

batchSize 属性控制运行多少个事务。如果未设置该属性,则批次大小由 Neo4j 服务器端定义。

Neo4j 5.7 中引入的 内部事务的错误策略 可通过 batchErrorPolicy 属性进行配置,支持的值为:- CONTINUE - BREAK - FAIL

自 Neo4j 5.21 起,内部事务还可以配置为 并行运行。布尔值 concurrent 属性控制该行为。并发默认禁用。

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:neo4j="http://www.liquibase.org/xml/ns/dbchangelog-ext"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

    <changeSet id="my-movie-init-oops" author="fbiville">
        <neo4j:cypher><![CDATA[CREATE (:Movie)<-[:VIEWED_BY {date: 'now'}]-(:Person)]]></neo4j:cypher>
        <neo4j:cypher><![CDATA[CREATE (:Movie)-[:VIEWED_BY {date: 'yesterday'}]->(:Dog)]]></neo4j:cypher>
    </changeSet>

    <changeSet id="my-movie-init-fixed" author="fbiville" runInTransaction="false">
        <neo4j:invertDirection type="VIEWED_BY" fragment="(:Person)-[r:VIEWED_BY]-&gt;()" outputVariable="r" enableBatchImport="true" batchSize="1" />
    </changeSet>

</databaseChangeLog>
{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "my-movie-init-oops",
        "author": "fbiville",
        "changes": [
          {
            "cypher": "CREATE (:Movie)<-[:VIEWED_BY {date: 'now'}]-(:Person)"
          },
          {
            "cypher": "CREATE (:Movie)-[:VIEWED_BY {date: 'yesterday'}]->(:Dog)"
          }
        ]
      }
    },
    {
      "changeSet": {
        "id": "my-movie-init-fixed",
        "author": "fbiville",
        "runInTransaction": false,
        "changes": [
          {
            "invertDirection": {
              "type": "VIEWED_BY",
              "fragment": "(:Person)-[r:VIEWED_BY]->()",
              "outputVariable": "r",
              "enableBatchImport": true,
              "batchSize": 1
            }
          }
        ]
      }
    }
  ]
}
databaseChangeLog:
  - changeSet:
      id: my-movie-init-oops
      author: fbiville
      changes:
        - cypher: 'CREATE (:Movie)<-[:VIEWED_BY {date: ''now''}]-(:Person)'
        - cypher: 'CREATE (:Movie)-[:VIEWED_BY {date: ''yesterday''}]->(:Dog)'
  - changeSet:
      id: my-movie-init-fixed
      author: fbiville
      runInTransaction: false
      changes:
        - invertDirection:
            type: 'VIEWED_BY'
            fragment: '(:Person)-[r:VIEWED_BY]->()'
            outputVariable: 'r'
            enableBatchImport: true
            batchSize: 1

属性重命名#

[所需插件版本 : 4.25.1.1]

属性重命名重构允许重命名所有相关实体的属性,或仅针对节点或关系进行重命名。

如下图所示,重构的主要属性包括

  • from:现有属性的名称
  • to:新属性名称,替换现有名称
  • entityType:要匹配的相关实体的类型。选项:ALL(默认)、NODERELATIONSHIP

全局重命名#

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:neo4j="http://www.liquibase.org/xml/ns/dbchangelog-ext"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

    <changeSet id="my-movie-init-oops" author="fbiville">
        <neo4j:cypher><![CDATA[CREATE (:Movie {calendar_date: 'today'})-[:SEEN_BY {calendar_date: 'now'}]->(:Person)]]></neo4j:cypher>
    </changeSet>

    <changeSet id="my-movie-init-fixed" author="fbiville">
        <neo4j:renameProperty from="calendar_date" to="date"  />
    </changeSet>

</databaseChangeLog>
{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "my-movie-init-oops",
        "author": "fbiville",
        "changes": [
          {
            "cypher": "CREATE (:Movie {calendar_date: 'today'})-[:SEEN_BY {calendar_date: 'now'}]->(:Person)"
          }
        ]
      }
    },
    {
      "changeSet": {
        "id": "my-movie-init-fixed",
        "author": "fbiville",
        "changes": [
          {
            "renameProperty": {
              "from": "calendar_date",
              "to": "date"
            }
          }
        ]
      }
    }
  ]
}
databaseChangeLog:
  - changeSet:
      id: my-movie-init-oops
      author: fbiville
      changes:
        - cypher: 'CREATE (:Movie {calendar_date: ''today''})-[:SEEN_BY {calendar_date: ''now''}]->(:Person)'
  - changeSet:
      id: my-movie-init-fixed
      author: fbiville
      changes:
        - renameProperty:
            from: 'calendar_date'
            to: 'date'

由于此操作可能会影响大量数据,在单个事务中运行该变更可能不可行,因为事务运行速度可能太慢,甚至可能导致内存溢出。

为了防止这种情况,必须将 enableBatchImport 设置为 true。由于它底层依赖于 CALL {} IN TRANSACTIONS,因此所包含 Change Set 的 runInTransaction 也必须设置为 false。这导致变更以批次方式执行。

警告

此设置仅在目标 Neo4j 实例支持 CALL {} IN TRANSACTIONS(4.4 及更高版本)时有效。如果不支持,Neo4j 插件将在单个自动提交事务中运行该变更。

请务必阅读有关 更改 runInTransaction 的后果

batchSize 属性控制运行多少个事务。如果未设置该属性,则批次大小由 Neo4j 服务器端定义。

Neo4j 5.7 中引入的 内部事务的错误策略 可通过 batchErrorPolicy 属性进行配置,支持的值为:- CONTINUE - BREAK - FAIL

自 Neo4j 5.21 起,内部事务还可以配置为 并行运行。布尔值 concurrent 属性控制该行为。并发默认禁用。

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:neo4j="http://www.liquibase.org/xml/ns/dbchangelog-ext"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

    <changeSet id="my-movie-init-oops" author="fbiville">
        <neo4j:cypher><![CDATA[CREATE (:Movie {calendar_date: 'today'})-[:SEEN_BY {calendar_date: 'now'}]->(:Person)]]></neo4j:cypher>
    </changeSet>

    <changeSet id="my-movie-init-fixed" author="fbiville" runInTransaction="false">
        <neo4j:renameProperty from="calendar_date" to="date" enableBatchImport="true" batchSize="1"  />
    </changeSet>

</databaseChangeLog>
{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "my-movie-init-oops",
        "author": "fbiville",
        "changes": [
          {
            "cypher": "CREATE (:Movie {calendar_date: 'today'})-[:SEEN_BY {calendar_date: 'now'}]->(:Person)"
          }
        ]
      }
    },
    {
      "changeSet": {
        "id": "my-movie-init-fixed",
        "author": "fbiville",
        "runInTransaction": false,
        "changes": [
          {
            "renameProperty": {
              "from": "calendar_date",
              "to": "date",
              "enableBatchImport": true,
              "batchSize": 1
            }
          }
        ]
      }
    }
  ]
}
databaseChangeLog:
  - changeSet:
      id: my-movie-init-oops
      author: fbiville
      changes:
        - cypher: 'CREATE (:Movie {calendar_date: ''today''})-[:SEEN_BY {calendar_date: ''now''}]->(:Person)'
  - changeSet:
      id: my-movie-init-fixed
      author: fbiville
      runInTransaction: false
      changes:
        - renameProperty:
            from: 'calendar_date'
            to: 'date'
            enableBatchImport: true
            batchSize: 1

仅节点属性重命名#

当将 entityType 属性设置为 NODE 时,只会重命名节点中匹配的属性

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:neo4j="http://www.liquibase.org/xml/ns/dbchangelog-ext"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

    <changeSet id="my-movie-init-oops" author="fbiville">
        <neo4j:cypher><![CDATA[CREATE (:Movie {calendar_date: 'today'})-[:SEEN_BY {calendar_date: 'now'}]->(:Person)]]></neo4j:cypher>
    </changeSet>

    <changeSet id="my-movie-init-fixed" author="fbiville">
        <neo4j:renameProperty from="calendar_date" to="date" entityType="NODE" />
    </changeSet>

</databaseChangeLog>
{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "my-movie-init-oops",
        "author": "fbiville",
        "changes": [
          {
            "cypher": "CREATE (:Movie {calendar_date: 'today'})-[:SEEN_BY {calendar_date: 'now'}]->(:Person)"
          }
        ]
      }
    },
    {
      "changeSet": {
        "id": "my-movie-init-fixed",
        "author": "fbiville",
        "changes": [
          {
            "renameProperty": {
              "from": "calendar_date",
              "to": "date",
              "entityType": "NODE"
            }
          }
        ]
      }
    }
  ]
}
databaseChangeLog:
  - changeSet:
      id: my-movie-init-oops
      author: fbiville
      changes:
        - cypher: 'CREATE (:Movie {calendar_date: ''today''})-[:SEEN_BY {calendar_date: ''now''}]->(:Person)'
  - changeSet:
      id: my-movie-init-fixed
      author: fbiville
      changes:
        - renameProperty:
            from: 'calendar_date'
            to: 'date'
            entityType: 'NODE'

由于此操作可能会影响大量数据,在单个事务中运行该变更可能不可行,因为事务运行速度可能太慢,甚至可能导致内存溢出。

为了防止这种情况,必须将 enableBatchImport 设置为 true。由于它底层依赖于 CALL {} IN TRANSACTIONS,因此所包含 Change Set 的 runInTransaction 也必须设置为 false。这导致变更以批次方式执行。

警告

此设置仅在目标 Neo4j 实例支持 CALL {} IN TRANSACTIONS(4.4 及更高版本)时有效。如果不支持,Neo4j 插件将在单个自动提交事务中运行该变更。

请务必阅读有关 更改 runInTransaction 的后果

batchSize 属性控制运行多少个事务。如果未设置该属性,则批次大小由 Neo4j 服务器端定义。

Neo4j 5.7 中引入的 内部事务的错误策略 可通过 batchErrorPolicy 属性进行配置,支持的值为:- CONTINUE - BREAK - FAIL

自 Neo4j 5.21 起,内部事务还可以配置为 并行运行。布尔值 concurrent 属性控制该行为。并发默认禁用。

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:neo4j="http://www.liquibase.org/xml/ns/dbchangelog-ext"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

    <changeSet id="my-movie-init-oops" author="fbiville">
        <neo4j:cypher><![CDATA[CREATE (:Movie {calendar_date: 'today'})-[:SEEN_BY {calendar_date: 'now'}]->(:Person)]]></neo4j:cypher>
    </changeSet>

    <changeSet id="my-movie-init-fixed" author="fbiville" runInTransaction="false">
        <neo4j:renameProperty from="calendar_date" to="date" entityType="NODE" enableBatchImport="true" batchSize="1" />
    </changeSet>

</databaseChangeLog>
{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "my-movie-init-oops",
        "author": "fbiville",
        "changes": [
          {
            "cypher": "CREATE (:Movie {calendar_date: 'today'})-[:SEEN_BY {calendar_date: 'now'}]->(:Person)"
          }
        ]
      }
    },
    {
      "changeSet": {
        "id": "my-movie-init-fixed",
        "author": "fbiville",
        "runInTransaction": false,
        "changes": [
          {
            "renameProperty": {
              "from": "calendar_date",
              "to": "date",
              "entityType": "NODE",
              "enableBatchImport": true,
              "batchSize": 1
            }
          }
        ]
      }
    }
  ]
}
databaseChangeLog:
  - changeSet:
      id: my-movie-init-oops
      author: fbiville
      changes:
        - cypher: 'CREATE (:Movie {calendar_date: ''today''})-[:SEEN_BY {calendar_date: ''now''}]->(:Person)'
  - changeSet:
      id: my-movie-init-fixed
      author: fbiville
      runInTransaction: false
      changes:
        - renameProperty:
            from: 'calendar_date'
            to: 'date'
            entityType: 'NODE'
            enableBatchImport: true
            batchSize: 1

仅关系属性重命名#

当将 entityType 属性设置为 RELATIONSHIP 时,只会重命名关系中匹配的属性

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:neo4j="http://www.liquibase.org/xml/ns/dbchangelog-ext"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

    <changeSet id="my-movie-init-oops" author="fbiville">
        <neo4j:cypher><![CDATA[CREATE (:Movie {calendar_date: 'today'})-[:SEEN_BY {calendar_date: 'now'}]->(:Person)]]></neo4j:cypher>
    </changeSet>

    <changeSet id="my-movie-init-fixed" author="fbiville">
        <neo4j:renameProperty from="calendar_date" to="date" entityType="RELATIONSHIP" />
    </changeSet>

</databaseChangeLog>
{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "my-movie-init-oops",
        "author": "fbiville",
        "changes": [
          {
            "cypher": "CREATE (:Movie {calendar_date: 'today'})-[:SEEN_BY {calendar_date: 'now'}]->(:Person)"
          }
        ]
      }
    },
    {
      "changeSet": {
        "id": "my-movie-init-fixed",
        "author": "fbiville",
        "changes": [
          {
            "renameProperty": {
              "from": "calendar_date",
              "to": "date",
              "entityType": "RELATIONSHIP"
            }
          }
        ]
      }
    }
  ]
}
databaseChangeLog:
  - changeSet:
      id: my-movie-init-oops
      author: fbiville
      changes:
        - cypher: 'CREATE (:Movie {calendar_date: ''today''})-[:SEEN_BY {calendar_date: ''now''}]->(:Person)'
  - changeSet:
      id: my-movie-init-fixed
      author: fbiville
      changes:
        - renameProperty:
            from: 'calendar_date'
            to: 'date'
            entityType: 'RELATIONSHIP'

由于此操作可能会影响大量数据,在单个事务中运行该变更可能不可行,因为事务运行速度可能太慢,甚至可能导致内存溢出。

为了防止这种情况,必须将 enableBatchImport 设置为 true。由于它底层依赖于 CALL {} IN TRANSACTIONS,因此所包含 Change Set 的 runInTransaction 也必须设置为 false。这导致变更以批次方式执行。

警告

此设置仅在目标 Neo4j 实例支持 CALL {} IN TRANSACTIONS(4.4 及更高版本)时有效。如果不支持,Neo4j 插件将在单个自动提交事务中运行该变更。

请务必阅读有关 更改 runInTransaction 的后果

batchSize 属性控制运行多少个事务。如果未设置该属性,则批次大小由 Neo4j 服务器端定义。

Neo4j 5.7 中引入的 内部事务的错误策略 可通过 batchErrorPolicy 属性进行配置,支持的值为:- CONTINUE - BREAK - FAIL

自 Neo4j 5.21 起,内部事务还可以配置为 并行运行。布尔值 concurrent 属性控制该行为。并发默认禁用。

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:neo4j="http://www.liquibase.org/xml/ns/dbchangelog-ext"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

    <changeSet id="my-movie-init-oops" author="fbiville">
        <neo4j:cypher><![CDATA[CREATE (:Movie {calendar_date: 'today'})-[:SEEN_BY {calendar_date: 'now'}]->(:Person)]]></neo4j:cypher>
    </changeSet>

    <changeSet id="my-movie-init-fixed" author="fbiville" runInTransaction="false">
        <neo4j:renameProperty from="calendar_date" to="date" entityType="RELATIONSHIP" enableBatchImport="true" batchSize="1" />
    </changeSet>

</databaseChangeLog>
{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "my-movie-init-oops",
        "author": "fbiville",
        "changes": [
          {
            "cypher": "CREATE (:Movie {calendar_date: 'today'})-[:SEEN_BY {calendar_date: 'now'}]->(:Person)"
          }
        ]
      }
    },
    {
      "changeSet": {
        "id": "my-movie-init-fixed",
        "author": "fbiville",
        "runInTransaction": false,
        "changes": [
          {
            "renameProperty": {
              "from": "calendar_date",
              "to": "date",
              "entityType": "RELATIONSHIP",
              "enableBatchImport": true,
              "batchSize": 1
            }
          }
        ]
      }
    }
  ]
}
databaseChangeLog:
  - changeSet:
      id: my-movie-init-oops
      author: fbiville
      changes:
        - cypher: 'CREATE (:Movie {calendar_date: ''today''})-[:SEEN_BY {calendar_date: ''now''}]->(:Person)'
  - changeSet:
      id: my-movie-init-fixed
      author: fbiville
      runInTransaction: false
      changes:
        - renameProperty:
            from: 'calendar_date'
            to: 'date'
            entityType: 'RELATIONSHIP'
            enableBatchImport: true
            batchSize: 1

Change Set 的 runInTransaction#

runInTransaction 的默认值为 true。这意味着给定 Change Set 的所有变更都在一个显式的、单一的事务中运行。

这是正确的默认设置,只有在需要以下两种 Cypher 构造之一时才应更改

事实上,在不禁用 runInTransaction 的情况下使用这些构造会失败,并出现类似的错误消息

A query with 'CALL { ... } IN TRANSACTIONS' can only be executed in an implicit transaction, but tried to execute in an explicit transaction.

在 Change Set 上将 runInTransaction 设置为 false 意味着其所有变更都将在其自己的自动提交(或隐式)事务中运行。

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">

    <changeSet id="my-movie-init" author="fbiville" runInTransaction="false">
        <sql>CALL { CREATE (:Movie {title: 'My Life', genre: 'Comedy'}) } IN TRANSACTIONS</sql>
    </changeSet>

</databaseChangeLog>
{
  "databaseChangeLog": [
    {
      "changeSet": {
        "id": "my-movie-init",
        "author": "fbiville",
        "runInTransaction": false,
        "changes": [
          {
            "cypher": "CALL { CREATE (:Movie {title: 'My Life', genre: 'Comedy'}) } IN TRANSACTIONS"
          }
        ]
      }
    }
  ]
}
databaseChangeLog:
  - changeSet:
      id: my-movie-init
      author: fbiville
      runInTransaction: false
      changes:
        - cypher: 'CALL { CREATE (:Movie {title: ''My Life'', genre: ''Comedy''}) } IN TRANSACTIONS'
-- liquibase formatted sql

-- changeset fbiville:my-movie-init runInTransaction:false
CALL { CREATE (:Movie {title: 'My Life', genre: 'Comedy'}) } IN TRANSACTIONS

历史一致性#

runInTransaction 是一个强大的工具,如果使用不当,可能会导致意外后果。

如果所包含 Change Set 的任何变更失败,则该 Change Set 不会存储在历史图中。

重新运行此 Change Set 会导致所有变更再次运行,即使是之前已经成功运行的变更也会再次执行。

在无法避免 runInTransactions="false" 的情况下,请确保受影响的 Change Set 查询是幂等的。定义 约束 并使用 Cypher 的 MERGE 而不是 CREATE 通常会有所帮助。

Neo4j 隔离级别温习#

CALL {} IN TRANSACTIONSPERIODIC COMMIT 会为每个批次生成一个新的事务。由于 Neo4j 的默认隔离级别是“read-committed”(读已提交),这些新事务可以读取先前事务修改的数据,无论它们是源自同一条语句还是完全不同的语句。

让我们用一个简单的例子来说明这一点。

假设数据初始化如下

CREATE (:Person {name: 'Alejandro'})
CREATE (:Person {name: 'Filipe'})
CREATE (:Person {name: 'Florent'})
CREATE (:Person {name: 'Marouane'})
CREATE (:Person {name: 'Nathan'})
这添加了 5 个具有 name 属性的 Person 标签节点。

运行 MATCH (p:Person) CALL { WITH p DELETE p } IN TRANSACTIONS OF 2 ROWS 可能会有不同的结果。该查询以每批 2 行的方式删除所有 Person 标签的节点。

如果数据未被其他并发事务修改,执行可能会成功并以 3 个批次运行。

如果并发事务创建了更多的 Person 节点,则可能需要更多的批次。

如果并发事务删除了 Person 节点,则可能需要更少的批次。

如果并发事务创建了指向上述任何节点的关系,CALL {} IN TRANSACTIONS 也可能会失败,因为 DELETE 假定节点是断开连接的(DETACH DELETE 会删除节点及其关系)。

© . This site is unofficial and not affiliated with Neo4j, Inc.