0

我刚刚开始了一个简单的 graphql 模式:

type Product {
    productID: ID!
    name: String @search(by: [term])
    reviews: [Review] @hasInverse(field: about)
}

type Review {
    id: ID!
    about: Product! @hasInverse(field: reviews)
    by: Customer! @hasInverse(field: reviews)
    comment: String @search(by: [fulltext])
    rating: Int @search
}

type Customer {
    custID: ID!
    name: String @search(by: [hash, regexp])
    reviews: [Review] @hasInverse(field: by)
}

现在我想用数百万个 json 条目填充数据库而不调用 graphql 突变(太慢)。例如,我有一个文件夹,里面装满了以下形状的几个 json 文件(客户和产品)。

json 客户文件示例:

{
id: "deadbeef",
name: "Bill Gates",
reviews: [
   {
      id:"1234",
      comment: "nice product"
      rating: 5,
      productId: "5678"
   }
]
}

json 产品文件示例:

{
id: "5678",
name: "Bluetooth headset",
}

据我了解,要定义节点之间的边,我首先必须用一个重载每个对象uid

客户将成为:

{
id: "deadbeef",
uid: "_:deadbeef",
...
reviews: [
   {
      id:"1234",
      uid:"_:1234",
      productId: {uid: "_:5678"}
   }
]
}

和产品

{
id: "5678",
uid: "_:5678"
...
}

然后我们可以批量导入它们(这纯粹是猜测,我从未尝试过)。虽然这应该导入条目,但我想知道数据库如何将这些条目与类型相关联,因为对于我们要插入的数据还没有任何线索。是否有像__typename我可以添加到每个条目中以键入它们的属性?

[编辑] 我找到了 2 个可能的属性classdgraph.type但仍然想知道哪一个以及我应该如何使用它们

4

2 回答 2

2

上面的 graphql 模式将生成以下谓词:

Customer.name
Customer.reviews
Product.name
Product.reviews
Review.about
Review.by
Review.comment
Review.rating
Schema.date
Schema.schema

Type.property批量导入值,不需要指定类型,使用正确的属性名即可。

这是一个工作示例:

    const product = {
        "dgraph.type":"Product",
        "uid": "_:5678",
        "Product.name": "Bluetooth headset"
    };

    const customer = {
        "uid": "_:deadbeef",
        "dgraph.type":"Customer",
        "Customer.name": "Bill Gates",
        "Customer.reviews": [
            {                    
                "uid": "_:1234",
                "dgraph.type":"Review",
                "Review.comment": "nice product",
                "Review.rating": 5,
                "Review.by": {"uid": "_:deadbeef"},
                "Review.about": {"uid": "_:5678"}
            }
        ]
    };

    // Run mutation.
    const mu = new Mutation();
    mu.setSetJson({set: [product, customer]});

如果要导入包含数千个条目的块,则需要找到一种方法来在交易中保留空白 ID。为了实现这一点,我建议使用一个类来负责在导入的块之间保存地图。这是我的 POC

import {DgraphClient, DgraphClientStub, Mutation} from "dgraph-js";
import * as jspb from 'google-protobuf';

type uidMap = jspb.Map<string, string>;

class UidMapper {

    constructor(private uidMap: uidMap = UidMapper.emptyMap()) {
    }

    private static emptyMap(): uidMap {
        return new jspb.Map<string, string>([]);
    }

    public uid(uid: string): string {
        return this.uidMap.get(uid) || `_:${uid}`;
    }

    public addMap(anotherMap: uidMap): void {
        anotherMap.forEach((value, key) => {
            this.uidMap.set(key, value);
        });
    }
}

class Importer {
    public async importTest(): Promise<void> {
        try {
            const clientStub = new DgraphClientStub(
                "localhost:9080",
                grpc.credentials.createInsecure(),
            );
            const dgraphClient: DgraphClient = new DgraphClient(clientStub);

            await this.createData(dgraphClient);

            clientStub.close();
        } catch (error) {
            console.log(error);
        }
    }

    private async createData(dgraphClient: DgraphClient): Promise<void> {
        const mapper = new UidMapper();

        const product = {
        "dgraph.type":"Product",
        "uid": mapper.uid("5678"),
        "Product.name": "Bluetooth headset"
        };

        const customer = ...;
        const addMoreInfo = ...;

        await this.setJsonData(dgraphClient, mapper, [product, customer]);
        await this.setJsonData(dgraphClient, mapper, [addMoreInfo]);
    }

    private async setJsonData(dgraphClient: DgraphClient, mapper: UidMapper, data: any[]) {
        // Create a new transaction.
        const txn = dgraphClient.newTxn();
        try {
            // Run mutation.
            const mu = new Mutation();

            mu.setSetJson({set: data});
            let response = await txn.mutate(mu);
            // Commit transaction.
            mapper.addMap(response.getUidsMap());
            await txn.commit();

        } finally {
            // Clean up. Calling this after txn.commit() is a no-op and hence safe.
            await txn.discard();
        }
    }
}
于 2019-11-04T21:51:45.973 回答
1

需要考虑的几点:

1 - GraphQL 和GraphQL+-是完全不同的东西。

2 - Dgraph 有一个需要遵循的类型系统。https://docs.dgraph.io/query-language/#type-system

3 - 客户端上的变异操作不相互连接,除了 Upsert 操作。https://docs.dgraph.io/mutations/#upsert-block 即在突变操作中设置 blank_node 不会将分配给它的值转移给下一个突变。您需要将分配的 UID 保存在一个变量中,然后在下一个突变中使用它。

有关突变和空白节点的更多信息https://tour.dgraph.io/master/intro/5/

4 - 如果您需要使用 GraphQL 层,您需要阅读此功能的所有帖子和建议。并了解 Dgraph 以一种方式工作,而 GraphQL 层以另一种方式工作。

继续。

如果您需要以 JSON 格式提交多个批次。我建议您使用 LiveLoad https://docs.dgraph.io/deploy/#live-loader。并使用 -x 标志。有了它,您可以保留创建的每个空白节点的 UID 映射。也就是说,如果您拥有的所有实体都有一个 Blank_node。它将被映射并分配一个 UID,然后通过 liveload 为每个新批次重用该 UID。

-x, --xidmap string            Directory to store xid to uid mapping

顺便说一句:我不知道 Dgraph 中“类”的概念。

我希望它有所帮助。

干杯。

于 2019-11-05T14:54:53.353 回答