跳到内容

功能#

Cypher 变更日志格式#

[所需插件版本 : 4.9.0.1]

该扩展支持多格式变更日志(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 部署是社区版 (Community Edition) 还是企业版 (Enterprise Edition)。它可以与标准布尔运算符结合其他前置条件使用。

<?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

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

加载数据#

[所需 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 String DLxmEfVUC9CAmjiNyVphWw== (base64 编码) byte[]
BOOLEAN Boolean truefalse Boolean
CLOB String String
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 String "a string" String
UUID String 1bc59ddb-8d4d-41d0-9c9a-34e837de5678 String

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

警告

目前不支持 BLOB 文件(参见问题)、CLOB 文件(参见问题)、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,因此 enclosing 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,因此 enclosing 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,因此 enclosing 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,因此 enclosing 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,因此 enclosing 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,因此 enclosing 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,因此 enclosing 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,因此 enclosing 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,因此 enclosing 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

变更集的 runInTransaction#

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

这是正确的默认值,仅当您需要以下两个 Cypher 构造之一时才应更改

事实上,在使用这些构造而未禁用 runInTransaction 时,会失败并出现类似错误消息

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

将变更集上的 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 是一个锋利的工具,可能导致意想不到的后果。

如果 enclosing change set 的任何变更失败,则该变更集将不会存储在历史图中。

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

在无法避免使用 runInTransactions="false" 的情况下,请确保受影响变更集的查询是幂等的。定义约束并使用 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 个标签为 Person 且带有 name 属性的节点。

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

如果在没有其他事务并发更改数据的情况下,执行可能会成功并运行 3 个批次。

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

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

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

© . All rights reserved.