索引和约束

这是 GraphQL 库版本 6 的文档。有关长期支持 (LTS) 版本 5,请参阅 GraphQL 库版本 5 LTS.

本页介绍如何在 Neo4j GraphQL 库中使用索引和约束。

唯一节点属性约束

唯一节点属性约束映射到类型定义中使用的 @unique 指令,其定义如下

"""Informs @neo4j/graphql that there should be a uniqueness constraint in the database for the decorated field."""
directive @unique(
    """The name which should be used for this constraint. By default; type name, followed by an underscore, followed by the field name."""
    constraintName: String
) on FIELD_DEFINITION

使用此指令不会自动确保这些约束的存在,您需要手动创建它们。

用法

@unique 指令只能在表示节点的 GraphQL 对象类型中使用,用于它们上指定的任何标签。

在以下示例中,对标签 Colour 和属性 hexadecimal 断言了一个唯一约束

type Colour @node {
    hexadecimal: String! @unique
}

在下一个示例中,对标签 Colour 和属性 hexadecimal 断言了一个名为 unique_colour 的唯一约束

type Colour @node {
    hexadecimal: String! @unique(constraintName: "unique_colour")
}

@node 指令用于在下一个示例中更改数据库标签映射,因此对列表中的第一个标签 Color 和属性 hexadecimal 断言了一个唯一约束

type Colour @node(labels: ["Color"]) {
    hexadecimal: String! @unique
}

在以下示例中,断言约束时还会检查 @node 指令的 labels 参数中指定的 所有 标签。如果对具有 Hue 标签的节点的 hexadecimal 属性指定了一个唯一约束,但没有对 Color 标签指定唯一约束,则在运行 assertIndexesAndConstraints 时不会抛出错误。

type Colour @node(labels: ["Color", "Hue"]) {
    hexadecimal: String! @unique
}

全文索引

您可以使用 @fulltext 指令在 Neo4j 中指定一个 全文索引。例如

input FullTextInput {
  indexName: String
  queryName: String
  fields: [String]!
}

"""
Informs @neo4j/graphql that there should be a fulltext index in the database, allows users to search by the index in the generated schema.
"""
directive @fulltext(indexes: [FullTextInput]!) on OBJECT

使用此指令不会自动确保这些索引的存在。它们必须手动创建。

指定

@fulltext 指令可以在节点上使用。在此示例中,为 Product 节点的 name 字段指定了一个名为“ProductName”的全文索引

type Product @fulltext(indexes: [{ indexName: "ProductName", fields: ["name"] }]) @node {
    name: String!
    color: Color! @relationship(type: "OF_COLOR", direction: OUT)
}

可以通过运行以下 Cypher 在数据库中创建此索引

CREATE FULLTEXT INDEX ProductName FOR (n:Product) ON EACH [n.name]

用法

对于指定的每个索引,库都会生成一个新的顶级查询。例如,对于之前的类型定义,将生成以下查询和类型

type Query {
    productsFulltextProductName(phrase: String!, where: ProductFulltextWhere, sort: [ProductFulltextSort!],
    limit: Int, offset: Int): [ProductFulltextResult!]!
}

"""The result of a fulltext search on an index of Product"""
type ProductFulltextResult {
  score: Float
  product: Product
}

"""The input for filtering a fulltext query on an index of Product"""
input ProductFulltextWhere {
  score: FloatWhere
  product: ProductWhere
}

"""The input for sorting a fulltext query on an index of Product"""
input ProductFulltextSort {
  score: SortDirection
  product: ProductSort
}

"""The input for filtering the score of a fulltext search"""
input FloatWhere {
  min: Float
  max: Float
}

然后,可以使用此查询执行 Lucene 全文查询 以匹配和返回产品。这是一个示例

query {
  productsFulltextProductName(phrase: "Hot sauce", where: { score: { min: 1.1 } } sort: [{ product: { name: ASC } }]) {
    score
    product {
      name
    }
  }
}

此查询以以下格式生成结果

{
  "data": {
    "productsFulltextProductName": [
      {
        "score": 2.1265015602111816,
        "product": {
          "name": "Louisiana Fiery Hot Pepper Sauce"
        }
      },
      {
        "score": 1.2077560424804688,
        "product": {
          "name": "Louisiana Hot Spiced Okra"
        }
      },
      {
        "score": 1.3977186679840088,
        "product": {
          "name": "Northwoods Cranberry Sauce"
        }
      }
    ]
  }
}

此外,还可以使用 queryName 参数在 @fulltext 指令中定义自定义查询名称

type Product @fulltext(indexes: [{ queryName: "CustomProductFulltextQuery", indexName: "ProductName", fields: ["name"] }]) @node {
    name: String!
    color: Color! @relationship(type: "OF_COLOR", direction: OUT)
}

这将生成以下顶级查询

type Query {
    CustomProductFulltextQuery(phrase: String!, where: ProductFulltextWhere, sort: [ProductFulltextSort!],
    limit: Int, offset: Int): [ProductFulltextResult!]!
}

然后,可以使用此查询,如下所示

query {
  CustomProductFulltextQuery(phrase: "Hot sauce", sort: [{ score: ASC }]) {
    score
    product {
      name
    }
  }
}

断言约束

为了确保指定的约束存在于数据库中,您需要运行函数 assertIndexesAndConstraints。一个检查必要约束是否存在的基本示例可能如下所示,假设变量 driver 中有一个有效的驱动程序实例。这将检查为使用 @unique 装饰的字段断言的唯一节点属性约束,并检查 @fulltext 中指定的索引

const typeDefs = `#graphql
    type Color @node {
        id: ID! @id
        hexadecimal: String! @unique
    }

    type Product @fulltext(indexes: [{ indexName: "ProductName", fields: ["name"] }]) @node {
        name: String!
        color: Color! @relationship(type: "OF_COLOR", direction: OUT)
    }
`;

const neoSchema = new Neo4jGraphQL({ typeDefs, driver });

const schema = await neoSchema.getSchema();

await neoSchema.assertIndexesAndConstraints();

使用 @vector GraphQL 指令,您可以查询数据库以执行向量索引搜索。查询是通过传入向量索引或查询短语来执行的。

通过向量索引的查询查找向量嵌入与该索引相似的节点。也就是说,查询执行最近邻搜索。

相反,通过短语(一段文本)的查询会将短语转发到 Neo4j GenAI 插件,该插件会为其生成向量嵌入。然后将此嵌入与数据库中的节点向量嵌入进行比较。

先决条件
  • 数据库必须是 Neo4j 版本 5.15 或更高版本。

  • 节点向量嵌入已经存在于数据库中。有关如何在 Cypher 和 Neo4j 中了解更多关于向量索引的信息,请参阅 向量索引

  • 嵌入必须使用相同的方法创建,即使用相同的提供程序和模型。有关如何在 Cypher 和 Neo4j 中了解更多关于向量嵌入的信息,请参阅 嵌入和向量索引教程

  • 不能跨多个标签执行通过向量索引的查询。

  • 通过短语的查询需要 Neo4j GenAI 插件的凭据。

向量索引搜索在以下意义上是 *只读* 的:查询操作的数据从数据库中检索,但不会被更改或写回数据库。

定义

"""Informs @neo4j/graphql that there should be a vector index in the database, allows users to search by the index in the generated schema."""
directive @vector(indexes: [VectorIndexInput]!) on OBJECT

VectorIndexInput 定义如下

input VectorIndexInput {
  """(Required) The name of the vector index."""
  indexName: String!
  """(Required) The name of the embedding property on the node."""
  embeddingProperty: String!
  """(Required) The name of the query."""
  queryName: String
  """(Optional) The name of the provider."""
  provider: String
}

如果可选字段 provider 被设置,则该类型用于通过短语进行查询,否则用于通过向量进行查询。provider 字段的允许值由可用的 GenAI 提供程序 定义。

用法

通过向量索引查询

通过传入向量来执行最近邻搜索,以查找向量嵌入与该向量相似的节点。

类型定义
type Product @node @vector(indexes: [{
  indexName: "productDescriptionIndex",
  embeddingProperty: "descriptionVector",
  queryName: "searchByDescription"
}]) {
  id: ID!
  name: String!
  description: String!
}

这定义了要在所有 Product 节点上执行的查询,这些节点具有 productDescriptionIndex 的向量索引,用于属性 descriptionVector,这意味着每个节点的 description 属性都已创建了向量嵌入。

示例查询
query FindSimilarProducts($vector: [Float]!) {
  searchByDescription(vector: $vector) {
    edges {
      cursor
      score
      node {
          id
          name
          description
      }
    }
  }
}

输入 $vector 是一个 FLOAT 值列表,应该类似于以下内容

示例向量
{
  "vector": [
    0.123456,
    ...,
    0.654321,
  ]
}

该查询返回所有Product节点,这些节点的descriptionVector属性上的向量嵌入与查询参数$vector相似。

按短语查询

执行使用Neo4j GenAI 插件的查询来为搜索短语创建向量嵌入,然后将其与数据库中节点上现有的向量嵌入进行比较。

需要插件的凭据。

确保在调用 Neo4jGraphQL 时设置了你的提供商凭据,例如

功能配置
const neoSchema = new Neo4jGraphQL({
    typeDefs,
    driver,
    features: {
        vector: {
            OpenAI: {
                token: "my-open-ai-token",
                model: "text-embedding-3-small",
            },
        },
    },
});

OpenAI是用于生成向量嵌入的 GenAI 提供商之一。有关提供商的完整列表及其各自的标识符,请参见GenAI 提供商

类型定义
type Product @node @vector(indexes: [{
  indexName: "productDescriptionIndex",
  embeddingProperty: "descriptionVector",
  provider: OPEN_AI,  # Assuming this is configured in the server
  queryName: "searchByPhrase"
}]) {
  id: ID!
  name: String!
  description: String!
}

这定义了要在所有 Product 节点上执行的查询,这些节点具有 productDescriptionIndex 的向量索引,用于属性 descriptionVector,这意味着每个节点的 description 属性都已创建了向量嵌入。

示例查询
query SearchProductsByPhrase($phrase: String!) {
  searchByPhrase(phrase: $phrase) {
    edges {
      cursor
      score
      node {
          id
          name
          description
      }
    }
  }
}

首先,查询将查询短语参数$phrase传递给 GenAI 插件,并让它为该短语生成一个向量嵌入。然后,它返回所有Product节点,这些节点在其descriptionVector属性上的向量嵌入与插件生成的向量嵌入相似。