3

I have a city name field I am getting value from (i.e. randomly formatted), such as "NEW YORK", "new york" or "new-york" or "ny".

Having that field's value, I need to present user with a list of cities that have that value in their name in this format:
NY – New York City – 10001
MN – New York Mills – 56567

Efficiency and performance is important. I can use JavaScript or PHP in order to retrieve this data.

So far, I've tried to use Freebase API. Here's an example query I've used: http://tinyurl.com/3ogpf7k

And here's JavaScript code I am using:

// App.q contains city name field's value

var query = {
    extended : 1,
    query : [{
        search : App.q,
        type : '/location/citytown',
        id : null,
        name : null,
        postal_codes : [],
        "/location/location/containedby" : [{
            "type" : "/location/us_state",
            "name": null,
            // two letters abbreviation of state
            "/common/topic/alias" : []
        }]
    }]
},
url = 'http://www.freebase.com/api/service/mqlread?callback=?&query=' + 
      encodeURIComponent(JSON.stringify(query));

$.getJSON(url, function( data ) {
    var cities, i, len, name, aliases, j, abbr, postal_code, HTML = [];

    for ( i = 0, len = data.result.length; i < len; i += 1 ) {
        // cities with names matching user's input
        if ( data.result[ i ].name.toLowerCase().indexOf( App.q ) === -1 ) continue;
        // cities that have postal_codes data available
        if ( data.result[ i ].postal_codes.length === 0 ) continue;

        name = data.result[ i ].name;
        aliases = data.result[ i ]["/location/location/containedby"][0]["/common/topic/alias"];
        postal_code = data.result[ i ].postal_codes[0];

        // find two-letters alias
        for ( j = aliases.length - 1; j >= 0; j -= 1 ) {
            if ( aliases[j].length === 2 && aliases[j] === aliases[j].toUpperCase() ) {
                 abbr = aliases[j];
            }
        }

        HTML.push( D.renderHTML( liTemplate, {
            name : name,
            abbr : abbr,
            postal_code : postal_code
        }) );

    }

    console.log( HTML.join('') );
});

However, querying "New York" doesn't seem to return even "New York City".

Is there a better API that satisfies my need? Am I doing something wrong with Freebase API?

UPD. Another helpful API that allows to do kind of the same (it still doesn't cut my needs though – too many results and no way of paging through them):
Docs: http://www.geonames.org/export/web-services.html
Example: http://api.geonames.org/postalCodeSearch?placename=los%20angeles&username=demo&maxRows=500

Thanks!

Misha Reyzlin
  • 13,736
  • 4
  • 54
  • 63
  • 2
    Associating a city with a postal code would not work in the UK, it would only give you the first few characters of the code. Another point is that there are many addresses that are not associated with a nearby city so the city field of an address form is not applicable. – koan Jun 05 '11 at 13:13
  • 1
    Neither for the Netherlands. It works the other way around. If you got a postal code and a house number, you can get the city and street from that. To my knowledge there is no open database or free api to use that database, although you can buy a license to that database and get periodic updates. – GolezTrol Jun 05 '11 at 13:15
  • I don't think it will work for *any* place that has large cities. In the US, even not-very-large towns often have multiple zip codes, and it's very common for blocks of codes to be allocated to "mail boxes" in post offices (don't know the international term; secure boxes with locks inside post office buildings). – Pointy Jun 05 '11 at 13:18
  • Though these are certainly valid points, my employer wasn't initially concerned with non-US addresses, i18n is more of my own curiosity. I'll update the question to make it more specific. Sorry! – Misha Reyzlin Jun 05 '11 at 13:19
  • @Pointy I don't need exact place, I generally need this in order to work with some other location based API, that unfortunately requires postal code – but it's not necessary to make it 100% accurate – Misha Reyzlin Jun 05 '11 at 13:39
  • I think it's better to ask for postal codes instead of towns. Town names are not always unique, not even within states. What would you do if someone enters `Holland, WI` (See http://en.wikipedia.org/wiki/Holland,_Wisconsin_%28disambiguation%29)? There probably are more examples, but I don't know of any. – Arjan Jun 05 '11 at 14:02
  • @Arjan, unfortunately I can't change that – I have that value and I can't do anything about, that's the data I have to work with, due to context requirements. There are other apps that use the same input, to put it shortly. – Misha Reyzlin Jun 05 '11 at 14:14

1 Answers1

1

Sorry about the previous misleading answer. I didn't notice that you were using the MQL extension "search" rather than the "name" property.

The main problem is that the default ordering is not by search score (not sure why, but that's the way it is). Modifying your query like this should fix things:

"search":       {"query":"New York","score":null,"type_strict":"all"},
"sort":"-search.score",
"type":"/location/citytown",

The sort parameter is sorting in descending order by the "score" value returned from the "search" subquery. You could also use the mql_filter parameter for search to have it not even consider things which fail to meet your constraint (slightly more efficient).

The search extension is documented here and the document for the underlying search service that is uses are here

Tom Morris
  • 10,490
  • 32
  • 53
  • I've guessed that `search : "new york"` isn't exact match, but “string in string” match. This page: https://www.freebase.com/docs/mql_extensions/.type.object.search doesn't go too far explaining how to use "search" extension and what exactly does it do, or maybe I just couldn't understand it properly. Thanks for your reply, although this (using `search~=`): http://tinyurl.com/3wbhdg2 doesn't seem to work either (no NYC in the results). – Misha Reyzlin Jun 17 '11 at 12:57