prisma-archive / graphcool-templates

πŸ“— Collection of Graphcool Templates
MIT License
361 stars 100 forks source link

geocode - array position changes depending on google data set #14

Open 54Factory opened 7 years ago

54Factory commented 7 years ago

Hi, great work on the geocode function. More of a question than issue.... I am a newb to coding and Graph.cool so hopefully you could help out. When running that function the data in the array result may change positions... For instance an address that has a "neighborhood" type will shift the "locality" to [3], where as an address without a "neighborhood" type "locality" lives in the [2] position. Using static data I reworked some things from other questions on SO....to return values by type. My question is how would I implement the comparison function into your code? Thanks in advance for any help on this!

function getMatchedTypes() {
  let i,
      j,
      types;
  let address_component = {}
  // Loop through the Geocoder result set. Note that the results
// array will change as this loop can self iterate.
  for (i = 0; i < address_components.length; i++) {

    address_component = address_components[i];

    types = address_component.types;

    for (j = 0; j < types.length; j++) {
      if (types[j] === 'street_number') {
        streetNumber = address_component.long_name;
      }
      if (types[j] === 'route') {
        street = address_component.long_name;
      }
      if (types[j] === 'neighborhood') {
        neighborhood = address_component.long_name;
      }
      if (types[j] === 'locality') {
        city = address_component.long_name;
      }
      if (types[j] === 'administrative_area_level_1') {
        state = address_component.long_name;
      }
      if (types[j] === 'administrative_area_level_2') {
        county = address_component.long_name;
      }
      if (types[j] === 'postal_code') {
        zip = address_component.long_name;
      }
      if (types[j] === 'postal_code_suffix') {
        zip = address_component.long_name;
      }
      break;
    }
    // console.log(address_component);

  }
  const geoLocation = {
    // The Lat/Lng values are defined from response "formatted address object" - uncomment live
    // lat: location.lat,
    // lng: location.lng,
    street: `${streetNumber} ${street}`,
    city,
    neighborhood,
    county,
    state,
    zip
  }
  console.log(geoLocation);

}

getMatchedTypes(address_components);
marktani commented 7 years ago

My question is how would I implement the comparison function into your code?

You can pretty much run the same code, it's normal JS πŸ™‚

54Factory commented 7 years ago

I've tried but get the following.... And can't find in the logs?? Sorry to pester and the need for hand holding! But, once I get it....I will assist others and post all snippets!

"code": 5000, "message": "A function returned an unhandled error. Please check the logs for executionId 'cj49xg0wd1dyp0184zup9z7ct'", "requestId": "cj49xg0wd1dyp0184zup9z7ct"

54Factory commented 7 years ago

Here is the most current non-working I have....

require('isomorphic-fetch')

module.exports = function (event) {
  const baseUrl = 'https://maps.googleapis.com/maps/api/geocode/json'
  const apiKey = 'API KEY'

  // Here we're merging contents of three inputs – country, city and street.
  // You could also have this be just one freestyle address input

  // this RegEx replaces spaces with plus signs to fulfill the requirements of the Google geocode API
  const addressInput = [event.data.street, event.data.zip].join('+').replace(/\s/g, '+')
  const address = encodeURI(addressInput)

  // Let's create the url to call
  const apiUrl = `${baseUrl}?address=${address}&result_type=street_address&key${apiKey}`

  return fetch(apiUrl)
    .then((res) => res.json())
    .then((json) => {
      const location = json.results[0].geometry.location
      const addressComponents = json.results[0].address_components;
      let i,
          j,
          types;
      let address_component = {}
    // Loop through the Geocoder result set. Note that the results
    // array will change as this loop can self iterate.
  for (i = 0; i < address_components.length; i++) {
    address_component = address_components[i];
    types = address_component.types;
    for (j = 0; j < types.length; j++) {
      if (types[j] === 'street_number') {
        streetNumber = address_component.long_name;
      }
      if (types[j] === 'route') {
        street = address_component.long_name;
      }
      if (types[j] === 'neighborhood') {
        neighborhood = address_component.long_name;
      }
      if (types[j] === 'locality') {
        city = address_component.long_name;
      }
      if (types[j] === 'administrative_area_level_1') {
        state = address_component.long_name;
      }
      if (types[j] === 'administrative_area_level_2') {
        county = address_component.long_name;
      }
      if (types[j] === 'postal_code') {
        zip = address_component.long_name;
      }
      if (types[j] === 'postal_code_suffix') {
        zip = address_component.long_name;
      }
      break;
    }
    // console.log(address_component);

  }
  const geoLocation = {
    // The Lat/Lng values are defined from response "formatted address object" - uncomment live
    lat: location.lat,
    lng: location.lng,
    street: `${streetNumber} ${street}`,
    city,
    neighborhood,
    county,
    state,
    zip
  }
  //console.log(geoLocation);

}

      // Let's merge the existing location data with Google response
      const eventData = Object.assign(event.data, geoLocation);
      return {data: eventData}
    })
    .catch(err => {
      console.log(err)

    })
}
marktani commented 7 years ago

Could you additional post the mutation that you do to execute the function? Sorry for the delay, somehow I just saw your message right now 😞

54Factory commented 7 years ago

I am not returning the last .then is what I believe. The if statements can be changed to switch. I am having a hard time with the logs so my debugging is non-existent... Please keep I'm mind I am self-taught and have been at this only a few months! I apologize in advance for not really knowing what is going on but I am truly committed to making this work and sharing my solutions with the community.

const addMutation = gql
mutation AddCustomerAndLocation($zip: String!, $state: String!, $city: String!, $street: String!, $locationName: String!, $customer: LocationcustomerCustomer){
  createLocation(zip: $zip, state: $state, city: $city, street: $street, locationName: $locationName, customer: $customer){
    id
    locationName
    street
    city
    state
    zip
    customer{
      id
      firstname
      lastname
      email
      phone
    }
  }
}
;
54Factory commented 7 years ago

the whole project is here https://github.com/54Factory/54project

marktani commented 7 years ago

That's right, the missing logs is a problem we're looking into already, I'm sorry it's negatively affecting you. Thanks so much for helping out though!

Could you try this function instead? There indeed was a problem that you did not return in the last then statement:

require('isomorphic-fetch')

module.exports = function (event) {
  const baseUrl = 'https://maps.googleapis.com/maps/api/geocode/json'
  const apiKey = 'API KEY'

  // Here we're merging contents of three inputs – country, city and street.
  // You could also have this be just one freestyle address input

  // this RegEx replaces spaces with plus signs to fulfill the requirements of the Google geocode API
  const addressInput = [event.data.street, event.data.zip].join('+').replace(/\s/g, '+')
  const address = encodeURI(addressInput)

  // Let's create the url to call
  const apiUrl = `${baseUrl}?address=${address}&result_type=street_address&key${apiKey}`

  return fetch(apiUrl)
    .then((res) => res.json())
    .then((json) => {
      const location = json.results[0].geometry.location
      const addressComponents = json.results[0].address_components;
      let i,
          j,
          types;
      let address_component = {}
      // Loop through the Geocoder result set. Note that the results
      // array will change as this loop can self iterate.
      for (i = 0; i < address_components.length; i++) {
        address_component = address_components[i];
        types = address_component.types;
        for (j = 0; j < types.length; j++) {
          if (types[j] === 'street_number') {
            streetNumber = address_component.long_name;
          }
          if (types[j] === 'route') {
            street = address_component.long_name;
          }
          if (types[j] === 'neighborhood') {
            neighborhood = address_component.long_name;
          }
          if (types[j] === 'locality') {
            city = address_component.long_name;
          }
          if (types[j] === 'administrative_area_level_1') {
            state = address_component.long_name;
          }
          if (types[j] === 'administrative_area_level_2') {
            county = address_component.long_name;
          }
          if (types[j] === 'postal_code') {
            zip = address_component.long_name;
          }
          if (types[j] === 'postal_code_suffix') {
            zip = address_component.long_name;
          }
          break;
        }
      }
      const geoLocation = {
        // The Lat/Lng values are defined from response "formatted address object" - uncomment live
        lat: location.lat,
        lng: location.lng,
        street: `${streetNumber} ${street}`,
        city,
        neighborhood,
        county,
        state,
        zip
      }
      return geoLocation
    }).then((geoLocation) =>
        // Let's merge the existing location data with Google response
        const eventData = Object.assign(event.data, geoLocation);
        return {data: eventData}
    })
    .catch(err => {
      console.log(err)
    })
}

Also, for me being able to try for myself, I would need the variable values for the mutation you posted above as well πŸ™‚


As a little meta information, formatting your code when posting it on GitHub is very helpful! You can use this syntax:

image

marktani commented 7 years ago

Ah, and another idea that increases the "feedback loop" for testing this function - use the Playground to trigger it, instead of your app πŸ™‚

54Factory commented 7 years ago

Thanks for all the tips! I need them! So that fails and this is the response...

Error
CODE
400
MESSAGE
Compilation failed: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode
ERROR
Block-scoped declarations (let, const, function, class) not yet supported outside strict mode
SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode
at Object.exports.runInThisContext (vm.js:53:16)
at WebtaskModule.compileWebtask (/data/sandbox/lib/module.js:91:32)
at Object.defaultJavascriptCompiler [as nodejsCompiler] (/data/sandbox/lib/compiler.js:124:30)
at module.exports.options.nodejsCompiler.fnCallback (/data/io/2bebdcda0af5491f85eb6ef218389da3/webtask.js:5:11)
at /data/sandbox/lib/compiler.js:230:17
at /data/sandbox/node_modules/async/dist/async.js:3830:24
at replenish (/data/sandbox/node_modules/async/dist/async.js:946:17)
at iterateeCallback (/data/sandbox/node_modules/async/dist/async.js:931:17)
at /data/sandbox/node_modules/async/dist/async.js:906:16
at /data/sandbox/node_modules/async/dist/async.js:3835:13
mutation testGoogleGeocoding {
  createLocation(
    street: "7 Foster Ave",
    city: "Havertown",
    state: "Pennsylvania",
    zip: "19083"
  ) {
    id
    street
    neighborhood
    city
    state
    zip
    lat
    lng
  }
}
54Factory commented 7 years ago

Also, in my Location Schema I would like to save the lat and lng in the same format as the geocode response. Such as this....geoLocation instead of lat and lng

geoLocation: {
         lat: 39.9444071,
         lng: -75.1631718
}
kbrandwijk commented 7 years ago

All variables inside the loop are undefined (streetnumber, street, etc.), or am I missing something?

kbrandwijk commented 7 years ago

I ran into this problem before, and I use this:

let geoResult = {}
addressComponents.forEach(e => e.types.forEach(t => Object.assign(geoResult, {[t]: e.long_name})))
})

That will create an object like this:

{ street_number: '1600',
  route: 'Amphitheatre Pkwy',
  locality: 'Mountain View',
  political: 'United States',
  administrative_area_level_2: 'Santa Clara County',
  administrative_area_level_1: 'California',
  country: 'United States',
  postal_code: '94043' }

That's a lot easier to work with!

kbrandwijk commented 7 years ago

Also, in my Location Schema I would like to save the lat and lng in the same format as the geocode response. Such as this....geoLocation instead of lat and lng

Create a JSON field geoLocation on the Location Type. Remove create permissions from that field if you only want to make it available as a result field, not input field. Then set it event.data.geoLocation = { long: ..., lat: ... }