0

Working on the website complement to an iOS app and my lack of javascript knowledge is showing and I could use some guidance.

My CloudKit database has at least two recordTypes: Events and Organization, each with multiple fields. The Organization recordType contains a reference to specific Events recordNames. I am trying to code a sequential query such that after each Event is called, its recordName will then be used to filter a query of Organization so that I can grab the related name, logo, etc. fields of organizations that reference the Event.

This approach works in the iOS code (XCode 7, Swift 2). I can successfully call each recordType in the knockoutjs code, so I know the access to the db is OK.

The code below is currently set up for the first function (fetchRecords) to call the second (fetchRecords2) and then merge the two sets of records. I'm sure this is a terrible structure but I've tried different approaches an am a bit lost right now.

     self.fetchRecords = function() {
        var query = { recordType: 'Events' };

        return publicDB.performQuery(query).then(function (response) {
            if(response.hasErrors) {
                console.error(response.errors[0]);
                return;
            }

            var records = response.records;
            var numberOfRecords = records.length;
            if (numberOfRecords === 0) {
                console.error('No matching items');
                return;
            }

            self.events(records);

            self.fetchRecords2();

        if (self.organization() != "") {
            self.events.push(self.organization);
            self.organization("");
            }

        });         
    };

    self.fetchRecords2 = function() {       
        var query2 = { recordType: 'Organization', filterBy: [{
                fieldName: 'events',
                comparator: 'EQUALS',
                fieldValue: {value: self.events.recordName}
            }] };

         return publicDB.performQuery(query2).then(function (response2) {
            if(response2.hasErrors) {
                console.error(response2.errors[0]);
                return;
            }

            var records2 = response2.records;
            var numberOfRecords2 = records2.length;
            if (numberOfRecords2 === 0) {
                console.error('No matching items');
                return;
            }

            self.organization(records2);


            });

        };

The HTML call is as follows:

<div data-bind="foreach: events">
  <div class="display">
    <h3><span data-bind="text: recordName"></span></h3> 
    <h3><span data-bind="text: fields.event_title.value"></span></h3>
    <h3><span data-bind="text: fields.date_of_event.value"></span></h3>
    <h3><span data-bind="text: fields.event_ratings.value"></span></h3> 
    <h3><span data-bind="text: fields.organization_name.value"></span></h3>                         
  </div>
</div>

I am seeing all of the Event fields (the first four), but the organization_name (coming from the Organization recordType) is causing errors to appear.

Any assistance or suggestions will be greatly appreciated.

RLW
  • 1
  • 2

1 Answers1

1

From your description, it looks like you have the following schema:

  1. Record Type "Organization" (with one or more CKReference fields to events)
  2. Record Type "Event"

My suggestion is to invert this relationship, i.e.:

  1. Record Type "Organization"
  2. Record Type "Event" (with a CKReference field to Organization)

This gives you the following benefits:

  • You can query for events and then fetch the organization for each event.
  • You can use reference actions:
    • 'VALIDATE': CloudKit ensures that the organization referenced by an event exists
    • 'DELETE_SELF': CloudKit deletes all related events when an organization is deleted
  • Adding a new event, doesn't change the organization's recordChangeTag

Example - Query events, then fetch organizations:

var organizations = {};
function getOrCreateOrganizationObject(orgRecordName) {
   if(!organizations[orgRecordName]) {
       //You'll want to create the new object the Knockout way
       organizations[orgRecordName] = {};
   }
   return organizations[orgRecordName];
}

//Query for events
publicDB.performQuery({recordType: 'Event'})
    .then(function(response) {
        var orgNamesToLookup = [];
        var events = response.records.map(function(record) {
            var orgRecordName = record.fields.organization.value.recordName;

            var organization = getOrCreateOrganizationObject(orgRecordName);

            var event = {
                recordName: record.recordName,
                title: record.fields.event_title.value,
                date: record.fields.date_of_event.value,
                rating: record.fields.event_ratings.value,
                //Note: You probably need some additional plumbing
                //      for this to work with Knockout
                organization: organization
            };
            return event;
        });

        //You can already show the list of events at this point, e.g.
        //something like: self.events(events)

        var orgRecordNames = Object.keys(organizations);
        return publicDB.fetchRecords(orgRecordNames)
            .then(function(response) {
                response.records.forEach(function(orgRecord) {
                    var organization = getOrCreateOrganizationObject[orgRecord.recordName];
                    organization.name = orgRecord.fields.name.value;
                });
            });
    })

Then, in your HTML, you would have something like this:

<div data-bind="foreach: events">
  <div class="display">
    <h3><span data-bind="text: recordName"></span></h3> 
    <h3><span data-bind="text: event_title"></span></h3>
    <h3><span data-bind="text: date_of_event"></span></h3>
    <h3><span data-bind="text: event_ratings"></span></h3> 
    <h3><span data-bind="text: organization.name"></span></h3>                         
  </div>
</div>

HTH

PS: In a real app you would want to also handle errors and the case when there are more events than get returned in the initial query.

Max Gunther
  • 296
  • 2
  • 5