Graphql + Dgraph, как пакетный импорт данных JSON? - PullRequest
0 голосов
/ 04 ноября 2019

Я только что начал тривиальную схему 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 возможных свойства class и dgraph.type, все еще задаваясь вопросом, какое из них и как мнеследует использовать их

Ответы [ 2 ]

2 голосов
/ 05 ноября 2019

Приведенная выше схема 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]});

Если вы хотите импортировать блоки из тысяч записей, вам нужно найти способ сохранить пустые идентификаторы в транзакциях. Для этого я предлагаю использовать класс, ответственный за сохранение карт среди импортируемых блоков. Вот мой 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();
        }
    }
}
1 голос
/ 05 ноября 2019

Некоторые моменты, которые необходимо учитывать:

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, который затем будет повторно использоваться для каждой новой партии через живую загрузку.

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

Кстати: я не знаю понятия «класс» в Dgraph.

Надеюсь, это поможет.

Приветствия.

...