【问题标题】:Graphql + Dgraph how to batch import json data?Graphql + Dgraph 如何批量导入json数据?
【发布时间】:2020-02-29 11:42:13
【问题描述】:

我刚刚开始了一个简单的 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",
}

据我所知,to defined edges between nodes,我首先必须用 uid 重载每个对象

客户会变成:

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

还有产品

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

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

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

【问题讨论】:

    标签: graphql dgraph


    【解决方案1】:

    上面的 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();
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      需要考虑的几点:

      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 保存在一个变量中,然后在下一个突变中使用它。

      更多关于突变和blank_node 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 中“类”的概念。

      希望对你有帮助。

      干杯。

      【讨论】:

      • 感谢您的回答,实际上 dgraph 最近支持原生 GraphQL graphql.dgraph.io(而不是 Graphql+-)我已经设法导入一些数据(使用 gRPC 客户端)然后我可以在 GraphQL 中查询,但这是一个 POC,我需要了解它是如何工作的,我对 dgraph 还是很陌生。此外,Dgraph 中的类似乎是以一种类名为前缀的谓词。具有 x 属性的 graphql 客户类型将生成谓词 Customer.x
      • +1 表示“您需要将分配的 UID 保存在一个变量中,然后在下一个突变中使用它”,因为我使用的是 js dgraph rpc 客户端,所以我将构建自己的 response.getUidsMap()保持地图跨越突变。
      • 嗨弗拉维恩。我为 Dgraph 工作,担任社区支持工程师。我提到了 Graphql+- 和 Graphql 的区别。因为在您的问题中,我了解到可能存在概念混淆。继续 - 您必须选择要使用的图层。或者 Dgraph 的原生语言或者 GraphQL 层。但你肯定需要同时掌握两者。 Dgraph 客户端不能直接使用 GraphQL。如果您使用 JS,我的建议是使用 Apollo Graphql 解决方案。在您的客户中。干杯。
      • 嗨,我说使用 ApolloClient 和原生 dgraph graphql 导入 250 万个条目(json 文件),但由于 gql 开销,导入速度呈指数级下降。因此,我专注于批处理 json 文件的真正解决方案。多亏了你,我的 POC 现在正在工作,我会尽快尝试将这些东西放在板凳上。
      • 为了提高性能,我尝试了live loader,它实际上比我的POC慢。我现在正在寻找使用批量导入器的正确方法
      猜你喜欢
      • 1970-01-01
      • 2017-07-17
      • 2011-03-07
      • 1970-01-01
      • 2018-07-29
      • 2015-04-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多