Возврат более глубоких отношений в Parse Cloud Code при использовании промисов

На данный момент я получил прекрасную информацию об обещаниях Parse и готов впитать в себя следующую часть головоломки для себя... отношения отношений. Следующий код по большей части отвечает на проблемы с Parse Cloud Code Promises а у меня в комментариях к разделам кусок кода моего следующего вопроса. Как мне продолжить запрашивать отношения внутри отношений верхнего уровня? было бы здорово посмотреть, как вы расширите свой код, чтобы сделать это @ roamer-1888

Последние:

//Return a single project
Parse.Cloud.define('getProjectByUrl', function(request, response) {
    var Projects = Parse.Object.extend("projects"); // with credit to @kRiZ
    var query = new Parse.Query(Projects);
    query.equalTo('projectUrl', request.params.projectUrl);
    query.include("projectStatus"); // *MJ Added the include to add the project Status pointer
    query.find().then(function(projectsResult) {
        var project = projectsResult[0];
        //From here, `project` is hierarchically monkey-patched with the results of further queries.

        //Make tags query, the referees query and the sections query in parallel.
        var tagsPromise = project.relation('tags').query().find();
        var refereesPromise = project.relation('referees').query().find();
        var sectionsPromise = project.relation('sections').query().include("sectionType").find(); //*MJ Added the include of the sectionType pointer

        // Aggregate the three promises with Parse.Promise.when(), and handle the responses.
        return Parse.Promise.when(tagsPromise, refereesPromise, sectionsPromise).then(function(tags, referees, sections) {
            project.set('tags', tags);
            project.set('referees', referees);
            project.set('sections', sections);

            //Go deeper into `sections`
            var sectionsRelationsPromises = sections.map(function(section) {
                // *MJ Remove sectionTypesPromise as it's a pointer
                var filesPromise = section.relation('files').query().include("fileType").find(); // *MJ Added the include to grab the fileType pointer
                return Parse.Promise.when(filesPromise).then(function(files) {
                    //*MJ Removed the promise for section Types
                    section.set('files', files);
                    // *MJ Removed the Deeper Logic
                });
            });
            return Parse.Promise.when(sectionsRelationsPromises);
        }).then(function() {
            return project;
        });
    }).then(function(project) {
        // At this point, all the data is gathered in the form of Parse objects in project,
        // which now needs to be mapped into a js plain object.
        var projectData = projectObj(project);
        projectData.tags = project.get('tags').map(tagObj); //Process the tags response
        projectData.referees = project.get('referees').map(refereeObj); // *MJ removed one e from refereeObj //Process the referees response
        projectData.sections = project.get('sections').map(sectionObj); //Process the sections response
        // *MJ Removed the adding of the files and looking over the sections.

        //Yay! (hopfully)
        response.success(projectData);
    }).fail(function(error) {
        response.error('Error: ' + error);
    });

    // ***********************************
    // ***** start: mapper functions *****
    // ***********************************
    function projectObj(project) {
        return { // *MJ Removed the stray equals sign here
            'id': project.id,
            'title': project.get('title'),
            'previewImage': project.get('previewImage'),
            'longDescription': project.get('longDescription'),
            'shortDescription': project.get('shortDescription'),
            'visibleToPublic': project.get('visibleToPublic'),
            'dateStart': project.get('dateStart'),
            'dateEnd': project.get('dateEnd'),
            'updatedAt': project.get('updatedAt'),
            "projectStatus": project.get("projectStatus").get("status") //*MJ Added the get of the Project Status status.
        }
    }
    function tagObj(tag) {
        return {
            'tag': tag.get('tag')
        };
    }
    function refereeObj(referee) {
        return {
            'name': referee.get('name'),
            'role': referee.get('role'),
            'emailAddress': referee.get('emailAddress'),
            'phoneNumber': referee.get('phoneNumber'),
            'linkedInUrl': referee.get('linkedInUrl')
        };
    }
    function sectionObj(section) {
        return {
            'type': section.get('sectionType').get("type"), // *MJ Added the pointer for SectionType > type
            'order': section.get('order'),
            'content': section.get('content'),
            'files': section.get('files').map(fileObj)
        };
    }
    function fileObj(file) {
        return {
            'name': file.get('name'), // *MJ chnaged the name to be more appropriate
            'url': file.get('url'), // *MJ Added the missing comma
            'type': file.get('fileType').get("type") //*MJ Added the pointer for fileType > type and removed semi colon
        };
    }
    // *********************************
    // ***** fin: mapper functions *****
    // *********************************
});

Старый:

//Return a single project
Parse.Cloud.define('getProject', function(request, response) {
    var Projects = Parse.Object.extend("projects"); // with credit to @kRiZ
    var query = new Parse.Query(Projects);
    query.equalTo('projectUrl', request.params.projectUrl);
    query.find().then(function(projectsResult) {
        var project = projectsResult[0];
        var projectData = {
            'id': project.id,
            'title': project.get('title'),
            'previewImage': project.get('previewImage'),
            'longDescription': project.get('longDescription'),
            'shortDescription': project.get('shortDescription'),
            'visibleToPublic': project.get('visibleToPublic'),
            'dateStart': project.get('dateStart'),
            'dateEnd': project.get('dateEnd'),
            'updatedAt': project.get('updatedAt')
        };

        //Now make the tags query and the referees query in parallel.
        var tagsPromise = project.relation('tags').query().find();
        var refereesPromise = project.relation('referees').query().find();
        var sectionsPromise = project.relation('sections').query().find();
        var sectionTypesPromise = project.relation('sections').query().find().relation('type').query().find();
        var filesPromise = project.relation('sections').query().find().relation('files').query().find();
        var fileTypesPromise = project.relation('sections').query().find().relation('files').query().find().relation('type').query().find();


        // Aggregate the two promises with Parse.Promise.when(), and handle the responses.
        return Parse.Promise.when(tagsPromise, refereesPromise, sectionsPromise, sectionTypesPromise, filesPromise, fileTypesPromise).then(function(tags, referees, sections, sectionTypes, files, fileTypes) {
            //Process the tags response
            projectData.tags = tags.map(function(tag) {
                return {
                    'tag': tag.get('tag')
                };
            });
            //Process the referees response
            projectData.referees = referees.map(function(refereee) {
                return {
                    'name': refereee.get('name'),
                    'role': refereee.get('role'),
                    'emailAddress': refereee.get('emailAddress'),
                    'phoneNumber': refereee.get('phoneNumber'),
                    'linkedInUrl': refereee.get('linkedInUrl')
                };
            });

            //Process the sections response
            projectData.sections = sections.map(function(section) {
                return {
                    'order': section.get('order'),
                    'content': section.get('content')
                };
            });

            // Problem: Sections have a relations column (files)
            // which I would like to return as a child of sections.
            // The files class then has a pointer column (type)
            // to another class which contains the a list of
            // file types (i.e. Image, Document, etc...)
            // The sections structure of projectDate should
            // look like:
            //
            // "sections": [{
            //   "type": "Section Type"
            //   "order": "1",
            //   "content": "This is the Section content",
            //   "files": [{
            //     "filename": "Image 1",
            //     "url": "image-1.png"
            //     "type": "Image"
            //   },
            //   {
            //     "filename": "Image 2",
            //     "url": "image-2.png",
            //     "type": "Image"
            //   },
            //   {
            //     ...
            //   }]
            //  },
            //  {
            //    ...
            //  }
            // }]

            //Process the section type response. This is reliant on sections being retrieved.
            projectData.sections.type = sections.map(function(sectionTypes) {
                return {
                    'type': sectionTypes.get('type')
                };
            });

            //Process the section files response. This is reliant on sections being retrieved.
            projectData.sections.files = sections.map(function(files) {
                return {
                    'filename': files.get('filename'),
                    'url': files.get('url')
                };
            });

            //Process the section files types response. This is reliant on files being retrieved.
            projectData.sections.files.type = sections.map(function(fileTypes) {
                return {
                    'type': fileTypes.get('type')
                };
            });
            // Currently not so Yay!

            response.success(projectData);
        });
    }).fail(function(error) {
        response.error('Error: ' + error);
    });
});

person Mark Johnston    schedule 08.12.2015    source источник
comment
Вау, я, должно быть, разблокировал уровень Attract придурков, которые не понимают, что люди учатся по-разному. Вы не знаете, что у меня есть и не пробовал. Возможно, если бы документация Parse была понятнее, мне было бы проще, но это не так, и из ответов, которые я видел, другим не совсем ясно, как правильно что-то делать. В вашем профиле даже не отображаются теги Parse, так что, похоже, вам лучше держаться подальше от темы, так как вам нечего добавить. Другие люди здесь полезны и готовы поделиться знаниями, которые у них есть.   -  person Mark Johnston    schedule 08.12.2015
comment
Статус Героя разблокирован!!! Хахаха. Уходи и позволь людям, желающим помочь другим, использовать мою полосу пропускания. Забавно, как вы удалили и отредактировали свои комментарии.   -  person Mark Johnston    schedule 08.12.2015
comment
В настоящее время не так Yay! заставил меня хихикать. Работаю над этим.   -  person Roamer-1888    schedule 08.12.2015
comment
Эй, если я не могу посмеяться над плохими навыками, мне будет грустно.   -  person Mark Johnston    schedule 10.12.2015


Ответы (2)


Идти глубже, чем на один уровень, немного сложнее. Каждому уровню нужен шаблон, очень похожий на шаблон верхнего уровня.

К сожалению, эти шаблоны не могут быть естественным образом «плоскими». Необходимость постоянно возвращаться к более ранним результатам делает вложение привлекательным, хотя, вероятно, и не абсолютно необходимым; Я уверен, что существуют и другие подходы.

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

//Return a single project
Parse.Cloud.define('getProject', function(request, response) {
    var Projects = Parse.Object.extend("projects"); // with credit to @kRiZ
    var query = new Parse.Query(Projects);
    query.equalTo('projectUrl', request.params.projectUrl);
    query.find().then(function(projectsResult) {
        var project = projectsResult[0];
        //From here, `project` is hierarchically monkey-patched with the results of further queries.

        //Make tags query, the referees query and the sections query in parallel.
        var tagsPromise = project.relation('tags').query().find();
        var refereesPromise = project.relation('referees').query().find();
        var sectionsPromise = project.relation('sections').query().find();

        // Aggregate the three promises with Parse.Promise.when(), and handle the responses.
        return Parse.Promise.when(tagsPromise, refereesPromise, sectionsPromise).then(function(tags, referees, sections) {
            project.set('tags', tags);
            project.set('referees', referees);
            project.set('sections', sections);

            //Go deeper into `sections`
            var sectionsRelationsPromises = sections.map(function(section) {
                var sectionTypesPromise = section.relation('type').query().find();
                var filesPromise = section.relation('files').query().find();
                return Parse.Promise.when(sectionTypesPromise, filesPromise).then(function(sectionTypes, files) {
                    section.set('type', sectionTypes[0]);
                    section.set('files', files);
                    //And deeper still into each of the section's files to find their types
                    var filesRelationsPromises = files.map(function(file) {
                        return file.relation('type').query().find().then(function(fileTypes) {
                            file.set('type', fileTypes[0]);
                        });
                    });
                    return Parse.Promise.when(filesRelationsPromises);
                });
            });
            return Parse.Promise.when(sectionsRelationsPromises);
        }).then(function() {
            return project;
        });
    }).then(function(project) {
        // At this point, all the data is gathered in the form of Parse objects in project,
        // which now needs to be mapped into a js plain object.
        var projectData = projectObj(project);
        projectData.tags = project.get('tags').map(tagObj); //Process the tags response
        projectData.referees = project.get('referees').map(refereeeObj); //Process the referees response
        projectData.sections = project.get('sections', sections).map(sectionObj); //Process the sections response
        projectData.sections.each(function(section) {
            section.files = section.get('files').map(fileObj);
        });
        //Yay! (hopfully)
        response.success(projectData);
    }).fail(function(error) {
        response.error('Error: ' + error);
    });

    // ***********************************
    // ***** start: mapper functions *****
    // ***********************************
    function projectObj(project) {
        return = {
            'id': project.id,
            'title': project.get('title'),
            'previewImage': project.get('previewImage'),
            'longDescription': project.get('longDescription'),
            'shortDescription': project.get('shortDescription'),
            'visibleToPublic': project.get('visibleToPublic'),
            'dateStart': project.get('dateStart'),
            'dateEnd': project.get('dateEnd'),
            'updatedAt': project.get('updatedAt')
        }
    }
    function tagObj(tag) {
        return {
            'tag': tag.get('tag')
        };
    }
    function refereeObj(referee) {
        return {
            'name': referee.get('name'),
            'role': referee.get('role'),
            'emailAddress': referee.get('emailAddress'),
            'phoneNumber': referee.get('phoneNumber'),
            'linkedInUrl': referee.get('linkedInUrl')
        };
    }
    function sectionObj(section) {
        return {
            'type': section.get('type'),
            'order': section.get('order'),
            'content': section.get('content'),
            'files': section.get('files').map(fileObj)
        };
    }
    function fileObj(file) {
        return {
            'filename': file.get('filename'),
            'url': file.get('url')
            'type': file.get('type');
        };
    }
    // *********************************
    // ***** fin: mapper functions *****
    // *********************************
});

Я сделал код настолько простым, насколько это возможно:

  • ограничивая первый обратный вызов .then асинхронным сбором данных.
  • организация сопоставления собранных данных в последующем обратном вызове .then.
  • вынос отдельных функций картографа в отдельный раздел в конце.

Поверьте мне, это более читабельно, чем делать все в одном блоке. Я попробовал это, и это был кошмар.

Удачной отладки.

person Roamer-1888    schedule 08.12.2015
comment
Привет, @roamer-1888. Я немного подчистил код и предпринял несколько разных попыток завершить его, но проблема проявляется в следующем: projectData.sections = project.get('sections', section).map(sectionObj); //Обработка ответа секций projectData.sections.each(function(section) { section.files = section.get('files').map(fileObj); }); //Ура! (надеюсь) я удалил вторые разделы из project.get('sections', section), потому что он сказал, что он не определен. Я изменил каждый на forEach, но затем ошибка: Object #‹Object› не имеет метода 'get'\n - person Mark Johnston; 10.12.2015
comment
Марк, я не удивлен, что часть глючила. Я был косоглазым к этому моменту. Я думаю, что на самом деле это стало проще, вызвав сопоставление файлов внутри сопоставления разделов. Я внес несколько правок выше, в том числе одну для section.type, которая также была неправильной. - person Roamer-1888; 10.12.2015
comment
Привет, @Roamer-1888, спасибо за правки. Я вернул в ОП как Последние свои задокументированные правки, я думаю, что это еще больше упрощается, и теперь я возвращаю JSON, на который я надеялся. Проверьте это и посмотрите, не сделал ли я что-нибудь явно глупое, и какие-либо другие отзывы, которые вы хотите дать. - person Mark Johnston; 10.12.2015
comment
Марк, я правильно понимаю, что это работает? Это, безусловно, выглядит так, как должно. Все выглядит вполне разумно, особенно мои исправленные синтаксические ошибки :-) Я посмотрел на .include() в документации и подумал, может ли он работать - намного лучше, чем я предложил. Единственное, что нужно улучшить — sections.map(...) теперь выполняет только один запрос на следующем уровне, поэтому Parse.Promise.when() не нужен — вы можете запустить цепочку промисов напрямую, return section.relation('files').query().include("fileType").find().then(...); - person Roamer-1888; 11.12.2015
comment
То, что я написал выше, сработало. Я просто немного занят сегодня, но я попробую эти предложенные правки, а затем выложу все это. Вы, безусловно, очень помогли мне лучше понять промисы. Я бы не сказал, что мое понимание еще не завершено, но оно находится в лучшем положении, чем до вашей помощи. - person Mark Johnston; 11.12.2015

Чтобы рассмотреть вопрос в целом: скажем, у вас есть класс с именем «ClassA», и у него есть отношение, называемое «classBs», связывающее его со многими экземплярами «ClassB». И скажите также, что экземпляры «ClassB» имеют отношение, называемое «classCs», связывающее их с экземплярами «ClassC».

ClassA --relates to many --< ClassB --relates to many --< ClassC
           ("classBs")                 ("classCs") 

Теперь, учитывая один экземпляр «ClassA», как можно получить все экземпляры «ClassC», связанные с ним, через «ClassB»?

Ответ прост, если помнить несколько вещей: (1) очень четко изложить модель данных, как это сделано здесь, (2) составить более сложную логику из более мелких и простых примитивов, возвращающих промисы (3) использовать Parse.Promise. или выполнить объединение нескольких запросов.

var _ = require("underscore");

// return a promise for many instances of ClassC related to the given aObject
function getCsFromA(aObject) {
    var bRelation = aObject.get("classBs");
    return bRelation.query().find().then(function(bObjects) {
        // gather all of the bObject's relation's queries
        var  queries = _.map(bObjects, function(bObject) {
            return aObject.get("classCs").query();
        });
        // get the union of many queries
        return Parse.Query.or(queries);
    });
}

Пара практических моментов: запросы по умолчанию ограничены 100, и вы можете изменить ограничение до 1000. Максимум, тысяча отношений из тысячи отношений — это миллион результатов. Это, безусловно, нарушит некоторые ограничения ресурсов на parse.com.

person danh    schedule 08.12.2015