Как я могу объединить данные с React native и множественными связями Firebase Firestore? - PullRequest
0 голосов
/ 19 февраля 2019

Я новичок, чтобы реагировать на нативный и firebase, но понимаю отношения SQL.Я пытаюсь понять, как смоделировать многие и многие отношения с помощью firebase в NoSQL.

В моем сценарии пользователи могут иметь несколько групп, а группы могут иметь несколько владельцев.

Я хочу отобразитьгрупповая информация (имя, город-штат) для конкретного пользователя.(В этом случае идентификатор документа: WQQZ6pcMMgQoTxH9cr2XGaPOq9W2).

Вот как структурируются данные в хранилище файлов Firebase:

{
    "__collections__": {
        "Group": {
            "group3": {
                "city": "Aurora",
                "state": "CO",
                "name": "Group Three",
                "__collections__": {}
            },
            "group4": {
                "state": "CO",
                "name": "Group Four",
                "city": "Denver",
                "__collections__": {}
            },
            "group5": {
                "city": "Aurora",
                "state": "CO",
                "name": "Group Five",
                "__collections__": {}
            }
        },
        "User": {
            "Hm56Zhn9TJP9jVYrzJRqHAB8T8H3": {
                "last_name": "Sneed",
                "first_name": "Sam",
                "__collections__": {}
            },
            "WQQZ6pcMMgQoTxH9cr2XGaPOq9W2": {
                "last_name": "Smith",
                "first_name": "Will",
                "__collections__": {}
            }
        },
        "UserGroups": {
            "F4GubhZKqWcJnHahL0TQTOP62Jj1": {
                "group3": true,
                "group4": true,
                "__collections__": {}
            },
            "WQQZ6pcMMgQoTxH9cr2XGaPOq9W2": {
                "group5": true
                "__collections__": {}
            }
        }
    }
}

Вот мой код:

export default class GroupSelect extends Component {
    constructor(props) {

        super(props);

        this.userGroupRef = firebase.firestore().collection("UserGroups").doc('WQQZ6pcMMgQoTxH9cr2XGaPOq9W2');

        this.state = {
            userGroups: [
                {
                    uid: 'group1',
                    name: 'Group One',
                    city: 'Chicago',
                    state: 'IL'
                },
                {
                    uid: 'group2',
                    name: 'Group Two',
                    city: 'Denver',
                    state: 'CO'
                }
            ]
        }
    }

    componentDidMount() {

        this.userGroupRef.get().then((doc) => {

            var obj = doc._data;

            var group_ids = Object.keys(obj).map(function (key) {
                return key;
            });

            var groups = [];

            var group = {};

            group_ids.forEach(function (group_id) {

                console.log(group_id);

                this.groupRef = firebase.firestore().collection('Group').doc(group_id);
                this.groupRef.get().then((groupDoc) => {
                    group.name = groupDoc._data['name'];
                    group.city = groupDoc._data['city'];
                    group.state = groupDoc._data['state'];
                    groups.push(group);
                });

            });

            console.log(groups); //not populated correctly

            // this.setState({
            //     userGroups: groups
            // });
        });
    }

    render() {
        return (
            this.state.userGroups.map((userGroup, index) => (
                <Text>{userGroup.name} - {userGroup.city}, {userGroup.state}</Text>
            ))
        )
    }
}

Если я закомментирую все в ComponentDidMount (), то render () правильно отобразит исходное содержимое state.UserGroups.Но, когда я пытаюсь заполнить массив в ComponentDidMount () и сбросить состояние userGroups var, возникает проблема с объединением и временем заполнения массива.

Как лучше всего это сделать?

Я описываю здесь, как здесь описываются объединения в firebase: https://www.youtube.com/watch?v=Idu9EJPSxiY

Users, eventAttendees and Events many-to-many join in firebase realtime db
Пользователи, eventAttendees и Events, многие-ко-многим, объединяются в firebaserealtime db

Но при этом используется база данных реального времени вместо хранилища файлов, что я и хочу использовать.

Я хочу сделать то же самое, но с: Users, UserGroups иГруппы

Должны ли данные структурироваться по-другому?


Я реализовал ответ Фрэнка ван Пуффелена ниже. Это работает, но это поднимает другие вопросы.

Если я использую коллекцию UserGroups, как описано ...

constructor(props) {

    super(props);

    this.userGroupRef = firebase.firestore().collection("UserGroups").doc(firebase.auth().currentUser.uid);

    this.state = {
        userGroups: []
    }
}

... она работает, но см. Возможные "проблемы" ниже.

componentDidMount() {

    this.userGroupRef.get().then((doc) => {

        var obj = doc._data;

        //get array of group ids
        var group_ids = Object.keys(obj).map(function (key) {
            return key;
        });

        let promises = [];

        //issue: making singleton database calls for each group id, maybe not good.
        group_ids.forEach(function (group_id) { 
            promises.push(firebase.firestore().collection('Group').doc(group_id).get())
        });

        let groups = [];

        let parentThis = this; //issue: get ref to parent this to set in Promise.all below, kinda weird, maybe not so clear

        Promise.all(promises).then(function(docs) {

            docs.forEach((groupDoc) => {
                let group = {};
                group.name = groupDoc._data['name'];
                group.city = groupDoc._data['city'];
                group.state = groupDoc._data['state'];
                groups.push(group);
            });

            parentThis.setState({
                userGroups: groups
            });
        });
    });
}

Если я реструктурирую данные и добавлю коллекцию групп ниже User ...

"User": {
    "WQQZ6pcMMgQoTxH9cr2XGaPOq9W2": {
        "last_name": "Carter",
        "first_name": "Will",
        "__collections__": {
            "Groups": {
                "group1": {
                    "city": "Chicago",
                    "state": "IL",
                    "name": "Group One",
                    "__collections__": {}
                },
                "group2": {
                    "city": "Denver",
                    "state": "CO",
                    "name": "Group Two",
                    "__collections__": {}
                }
            }
        }
    }
}

Затем код становится более простым.

constructor(props) {

    super(props);

    //path: User/WQQZ6pcMMgQoTxH9cr2XGaPOq9W2/Groups 
    this.userGroupRef = firebase.firestore().collection("User").doc(firebase.auth().currentUser.uid).collection("Groups");

    this.state = {
        userGroups: []
    }
}

componentDidMount() {

    this.userGroupRef.get().then((doc) => {

        let groups = [];

        doc._docs.forEach(function (groupDoc) {
            let group = {};
            group.name = groupDoc._data['name'];
            group.city = groupDoc._data['city'];
            group.state = groupDoc._data['state'];
            groups.push(group);
        });

        this.setState({
            userGroups: groups
        });

    });

}

Этот метод кажется мне чище.Но теперь у меня есть дубликаты данных в корневой коллекции Group и в разделе User collection.

Лучше ли делать это в этом методе с дублирующимися данными Group в БД?

У меня есть реляционная БДсправочная информация и изучение лучших практик NoSQL.Мой инстинкт не дублировать данные в БД.

Ответы [ 2 ]

0 голосов
/ 22 февраля 2019

Это видео может ответить на мой вопрос.... в 13:31: "это, вероятно, будут дубликаты данных, которые будут храниться как в пользовательском объекте верхнего уровня, так и в этом отдельном обзоре, и в будущих видеороликах мы поговорим о лучших стратегиях для обеспечения согласованности такого рода вещей. "

Это наводит меня на мысль, что я должен продублировать необходимые данные группы под пользователем, а не делать бит соединения в клиенте.https://www.youtube.com/watch?v=v_hR4K4auoQ

Или, может быть, нет: Из этого видео: https://www.youtube.com/watch?v=jm66TSlVtcc

enter image description here

... в 7:17 «Если у вас есть фон SQL, это очень похоже на использование промежуточной таблицы для объединений»

Подробнее здесь: https://angularfirebase.com/lessons/firestore-nosql-data-modeling-by-example/#Subcollection-Many-to-Many-Relationships

В приведенной выше структуре, какПолучите ли вы все твиты, которые понравились пользователю (на слух), используя таблицу сердец с внешними ключами в одном запросе?

0 голосов
/ 20 февраля 2019

Вы печатаете console.log(groups) за пределами обратного вызова, куда загружаются группы.Это не сработает, поскольку данные загружаются из Firestore асинхронно, и ваш оператор журнала запускается до загрузки каких-либо данных.

Это проще всего увидеть, если вы поместите несколько простых операторов журнала:

console.log("Start loading documents");
group_ids.forEach(function (group_id) {
    this.groupRef = firebase.firestore().collection('Group').doc(group_id);
    this.groupRef.get().then((groupDoc) => {
      console.log("Loaded document");
    });
});
console.log("Started loading documents");

Когда вы запускаете этот код, он выводит:

Начало загрузки документов

Начало загрузки документов

Загруженный документ

Загруженный документ

...

Вероятно, это не тот порядок, который вы ожидали, но он прекрасно объясняет, почему массив groups пуст при его печати: ни одна из групп не имеетбыл загружен еще.Фактически, если вы зарегистрируете groups insode обратного вызова then(), вы увидите, что он заполняется по одному документу за раз.

Любой код, которому нужны документы, должен быть либо внутри обратного вызова,или дождитесь загрузки документа (ов), используя обещание.Поскольку вы ожидаете нескольких документов, используйте Promise.all():

var promises = [];
group_ids.forEach(function (group_id) {
    promises.push(firebase.firestore().collection('Group').doc(group_id).get())
});
Promise.all(promises).then(function(docs) {
    docs.forEach((groupDoc) => {
        group.name = groupDoc._data['name'];
        group.city = groupDoc._data['city'];
        group.state = groupDoc._data['state'];
        groups.push(group);
    });
    console.log(groups);
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...