I'm trying to find people who were near to a user's last location in last 24 hours for a simple recommendation engine. My data set is simple for test purposes:
What is wrong with my query? It's returning user Ayse multiple times, but I don't want same person twice or more.
Here is my queries:
var neo4j = require('neo4j-driver').v1;
var driver = neo4j.driver("bolt://localhost", neo4j.auth.basic("neo4j", "neo4j"));
var session = driver.session();
function runQuery(query) {
session.run(query, {}).then(function(result){
console.log("finish")
result.records.forEach(function(record) {
console.log(record._fields)
})
}).catch(console.log);
}
//runQuery("MATCH (n) DETACH DELETE n" )
function newUser(user) {
runQuery("CREATE (:User{name:'"+user+"'})")
}
function newLocation(user, lat, lon) {
var q =
"MATCH (u:User{name:'"+user+"'}) "+
"CREATE (l:Location{lat: "+lat+", lon: "+lon+", created_at: TIMESTAMP()}) "+
"CREATE (u)-[:HAVE_BEEN]->(l) "
runQuery(q)
}
/*
newUser("Ozgur")
newUser("Fatma")
newUser("Ayse")
*/
/*
newLocation("Ozgur", 38.134972, 26.96681)
newLocation("Ozgur", 37.239972, 25.96681)
newLocation("Ozgur", 38.334972, 16.96681)
*/
/*
newLocation("Ayse", 38.294972, 26.76681)
newLocation("Ayse", 37.639972, 25.66681)
newLocation("Ayse", 35.134972, 18.96681)
*/
/*
newLocation("Fatma", 31.114972, 21.76681)
newLocation("Fatma", 31.139972, 21.66681)
*/
function findUserRecommendation(user) {
// get user recommendations
var q =
"MATCH "+
//get user locations for last 24 hours (me_loc)
"(me:User{name:'"+user+"'})-[:HAVE_BEEN]->(me_loc:Location), " +
// strangers (recommendations)
"(l:Location)<-[:HAVE_BEEN]-(stranger:User) " +
"WHERE "+
//except me
"NOT me = stranger "+
// nearest location algorithm based on KM
"AND 2 * 6371 * "+
"asin("+
"sqrt("+
"haversin(radians(me_loc.lat - l.lat)) + "+
"cos(radians(me_loc.lat)) * "+
"cos(radians(l.lat)) * haversin(radians(me_loc.lon - l.lon))"+
")"+
// 500 km
") < 500 " +
// last 24 hours of they location
//"AND l.created_at > (TIMESTAMP() - 604800) "+
// last 24 hours of user's location
//"AND me_loc.created_at > (TIMESTAMP() - 604800)" +
// return every stranger's name and last location once
// but returning Ayse's name and different location more than once
"RETURN DISTINCT stranger.name, l.lat, l.lon"
runQuery(q)
}
findUserRecommendation("Ozgur")
DISTINCT outside of an aggregation just ensures that you won't get duplicate result rows, it doesn't limit you to the first row for each DISTINCT element (it actually applies to all non-aggregated elements in the row as a unit, like using a tuple as a key). You'll have to aggregate the lat/long and then figure out which one of them to keep. Assuming you just want any single lat/long, try this in place of your return line:
WITH stranger, COLLECT(l) AS locs
WITH stranger, HEAD(locs) AS l
RETURN stranger.name, l.lat, l.lon
You can replace the HEAD() call with a REDUCE() if you want to pick out a single lat/long based on particular criteria, like most recent or closest.
Related
Background: I'm trying to get an event-time temporal join working with two 'large(r)' datasets/tables that are read from a CSV-file (16K+ rows in left table, somewhat less in right table). Both tables are append-only tables, i.e. their datasources are currently CSV-files, but will become CDC changelogs emitted by Debezium over Pulsar.
I am using the fairly new SYSTEM_TIME AS OF syntax.
The problem: join results are only partly correct, i.e. at the start (first 20% or so) of the execution of the query, rows of the left-side are not matched with rows from the right side, while in theory, they should. After a couple of seconds, there are more matches, and by the time the query ends, rows of the left side are getting matched/joined correctly with rows of the right side.
Every time that I run the query it shows other results in terms of which rows are (not) matched.
Both datasets are not ordered by their respective event-times. They are ordered by their primary key. So it's really this case, only with more data.
In essence, the right side is a lookup-table that changes over time, and we're sure that for every left record there was a matching right record, as both were created in the originating database at +/- the same instant. Ultimately our goal is a dynamic materialized view that contains the same data as when we'd join the 2 tables in the CDC-enabled source database (SQL Server).
Obviously, I want to achieve a correct join over the complete dataset as explained in the Flink docs
Unlike simple examples and Flink test-code with a small dataset of only a few rows (like here), a join of larger datasets does not yield correct results.
I suspect that, when the probing/left table starts flowing, the build/right table is not yet 'in memory' which means that left rows don't find a matching right row, while they should -- if the right table would have started flowing somewhat earlier. That's why the left join returns null-values for the columns of the right table.
I've included my code:
#Slf4j(topic = "TO_FILE")
public class CsvTemporalJoinTest {
private final String emr01Ddl =
"CREATE TABLE EMR01\n" +
"(\n" +
" SRC_NO STRING,\n" +
" JRD_ETT_NO STRING,\n" +
" STT_DT DATE,\n" +
" MGT_SLT_DT DATE,\n" +
" ATM_CRT_DT DATE,\n" +
" LTD_MDT_IC STRING,\n" +
" CPN_ORG_NO STRING,\n" +
" PTY_NO STRING,\n" +
" REG_USER_CD STRING,\n" +
" REG_TS TIMESTAMP,\n" +
" MUT_USER_CD STRING,\n" +
" MUT_TS TIMESTAMP(3),\n" +
" WATERMARK FOR MUT_TS AS MUT_TS,\n" +
" PRIMARY KEY (CPN_ORG_NO) NOT ENFORCED\n" +
") WITH (\n" +
" 'connector' = 'filesystem',\n" +
" 'path' = '" + getCsv1() + "',\n" +
" 'format' = 'csv'\n" +
")";
private final String emr02Ddl =
"CREATE TABLE EMR02\n" +
"(\n" +
" CPN_ORG_NO STRING,\n" +
" DSB_TX STRING,\n" +
" REG_USER_CD STRING,\n" +
" REG_TS TIMESTAMP,\n" +
" MUT_USER_CD STRING,\n" +
" MUT_TS TIMESTAMP(3),\n" +
" WATERMARK FOR MUT_TS AS MUT_TS,\n" +
" PRIMARY KEY (CPN_ORG_NO) NOT ENFORCED\n" +
") WITH (\n" +
" 'connector' = 'filesystem',\n" +
" 'path' = '" + getCsv2() + "',\n" +
" 'format' = 'csv'\n" +
")";
#Test
public void testEventTimeTemporalJoin() throws Exception {
var env = StreamExecutionEnvironment.getExecutionEnvironment();
var tableEnv = StreamTableEnvironment.create(env);
tableEnv.executeSql(emr01Ddl);
tableEnv.executeSql(emr02Ddl);
Table result = tableEnv.sqlQuery("" +
"SELECT *" +
" FROM EMR01" +
" LEFT JOIN EMR02 FOR SYSTEM_TIME AS OF EMR01.MUT_TS" +
" ON EMR01.CPN_ORG_NO = EMR02.CPN_ORG_NO");
tableEnv.toChangelogStream(result).addSink(new TestSink());
env.execute();
System.out.println("[Count]" + TestSink.values.size());
//System.out.println("[Row 1]" + TestSink.values.get(0));
//System.out.println("[Row 2]" + TestSink.values.get(1));
AtomicInteger i = new AtomicInteger();
TestSink.values.listIterator().forEachRemaining(value -> log.info("[Row " + i.incrementAndGet() + " ]=" + value));
}
private static class TestSink implements SinkFunction<Row> {
// must be static
public static final List<Row> values = Collections.synchronizedList(new ArrayList<>());
#Override
public void invoke(Row value, SinkFunction.Context context) {
values.add(value);
}
}
String getCsv1() {
try {
return new ClassPathResource("/GBTEMR01.csv").getFile().getAbsolutePath();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
String getCsv2() {
try {
return new ClassPathResource("/GBTEMR02.csv").getFile().getAbsolutePath();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Is there a way to solve this? E.g. is there a way to FIRST load the right side into Flink state, and THEN start loading/streaming the left side? Would this be a good approach because this question begs: how much later? what is the time that the left side can start flowing?
We're using Flink 1.13.3.
This sort of temporal/versioned join depends on having accurate watermarks. Flink relies on the watermarks to know which rows can safely be dropped from the state being maintained (because they can no longer affect the results).
The watermarking you've used indicates that the rows are ordered by MUT_TS. Since this isn't true, the join isn't able to produce complete results.
To fix this, the watermarks should be defined with something like this
WATERMARK FOR MUT_TS AS MUT_TS - INTERVAL '2' MINUTE
where the interval indicates how much out-of-orderness needs to be accommodated.
function doGet(e){
Logger.log("--- doGet ---");
var tag = "",
value = "";
try {
// this helps during debuggin
if (e == null){e={}; e.parameters = {tag:"test",value:"-1"};}
tag = e.parameters.tag;
value = e.parameters.value;
// save the data to spreadsheet
save_data(tag, value);
return ContentService.createTextOutput("Wrote:\n tag: " + tag + "\n value: " + value);
} catch(error) {
Logger.log(error);
return ContentService.createTextOutput("oops...." + error.message
+ "\n" + new Date()
+ "\ntag: " + tag +
+ "\nvalue: " + value);
}
}
// Method to save given data to a sheet
function save_data(tag, value){
Logger.log("--- save_data ---");
try {
var dateTime = new Date();
// Paste the URL of the Google Sheets starting from https thru /edit
// For e.g.: https://docs.google.com/..../edit
var ss = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/1HYJRHfJVgZp16xYt4fipR1bH2BudhuQ4UrDhAf1rBKw/edit");
var dataLoggerSheet = ss.getSheetByName("Datalogger");
// Get last edited row from DataLogger sheet
var row = dataLoggerSheet.getLastRow() + 1;
// Start Populating the data
dataLoggerSheet.getRange("A" + row).setValue(row -1); // ID
dataLoggerSheet.getRange("B" + row).setValue(dateTime); // dateTime
dataLoggerSheet.getRange("C" + row).setValue(tag); // tag
dataLoggerSheet.getRange("D" + row).setValue(value); // value
//dataLoggerSheet.getRange("E" + row).setValue(value); // value
//dataLoggerSheet.getRange("F" + row).setValue(value); // value
// Update summary sheet
summarySheet.getRange("B1").setValue(dateTime); // Last modified date
// summarySheet.getRange("B2").setValue(row - 1); // Count
}
catch(error) {
Logger.log(JSON.stringify(error));
}
Logger.log("--- save_data end---");
}
In the example above, inside the value of "tag" there is data in the format "12345678912 | ABCDEFGHIJ". "Tag" data in column C; I want to print with the first 11 characters in column D. The "tag" data in column C; I want to print it with column E after 12 characters. How can I do that?
To see my overall objective, these two screenshots should show it.
Original Spreadsheet from Form:
enter image description here
Formatted Version of Spreadsheet:
enter image description here
Thank you in advance for your help.
You will have to manipulate the tag string in order to get the values you want.
In order to do this you will have change this:
dataLoggerSheet.getRange("C" + row).setValue(tag); // tag
To this:
dataLoggerSheet.getRange("C" + row).setValue(tag.slice(0,tag.indexOf('|')); //first part of tag
dataLoggerSheet.getRange("D" + row).setValue(tag.slice(tag.indexOf('|') + 2)); // second part of tag
dataLoggerSheet.getRange("E" + row).setValue(value); // value
Explanation
The above snippet makes use of the indexOf and slice methods from JavaScript in order to get the index of the | character in order to be able to split the tag string into two different strings.
Reference
JavaScript Array.prototype.slice();
JavaScript Array.prototype.indexOf().
==================================
UPDATE 11 December 2019
My Question is more about Macro Script
The GOAL (in illustration)
to change below raw sheet:
to more readable format:
Basically what i'm doing is split the campaign name with the separator and parse it.
I don't have the problem if the function on only process single cell,for example:
on "Report" Sheet the CELL B2 , is taking data from "Data" B2 ONLY
i got problem when the return data require conditional operator that involve specific condition. So while processing cell B2, it require content from E2, D2, etc
=====================================
i'm taking data from Google Ads/Analytics API to Google Sheet on specific worksheet (i call it 'Raw Data').
Now i'm using pattern for the campaign, so i can easily split/break with separator in order for me to get specific data.
For Example:
With this, by using underscore as separator, i can split campaign name, into various data:
Campaign Objective: Sales
Campaign Title: TBMB
Network: SEM
Branch: All
Targeting: Keywords
..etc
Then i create new sheet called Called CReport which consist the same data from Raw Data sheet, but in much better visualization for marketing people.
Now, after searching on Google, i found the solution for self reference cell.
The script goes like this:
function getSegment(data,index){
temp=data.split("_");
return temp[index-1];
}
function dataParse(input,dataSegment){
return Array.isArray(input) ? input.map(function(e){
return e.map(function(f){
if(f!=""){
return getSegment(f,dataSegment);
}
}
)}
) : "false usage";
}
So if i want to have a column with Network Name, i can place this formula on row 2 (because row 1 is for table header) something like this:
=ArrayFormula(dataParse('RAW DATA'!B2:B;2))
Now my question:
This works for self-reference cell, means if the data taken from B2 in RAW DATA sheet, it will be the only data referenced to cell in Campaign Report sheet.
If the pointer is in B2 on CReport Sheet require data not only from B2 in RAW DATA but also D2 Cell.
What script i need to add in my function ?
i'm expecting the chunk of code will something like this
function dataParse(input,dataSegment){
return Array.isArray(input) ? input.map(function(e){
return e.map(function(f){
if(f!=""){
segmentData=getSegment(f,dataSegment);
if(segmentData=="google"){
returnData=get reference from column D //<---
}else{
returnData=get reference from column E //<---
}
return returnData
}
}
)}
) : "false usage";
}
Hope its clear enough.
Thanks in Advance !
I modified your function in this way:
// range (String): It will be used to get the info in a range
function dataParse(input,dataSegment, range){
var val = "";
return Array.isArray(input) ? input.map(function(e, index){
return e.map(function(f){
if(f!=""){
// If col D has value google then take info from col B
if(f === "google") val = getDesiredRangeValue("B", range, index);
// else take info from col E
else val = getDesiredRangeValue("E", range, index);
// Take segment as needed
return getSegment(val,dataSegment);
}
}
)}
) : "false usage";
}
In order to make it work, I inserted an extra argument to the function. Now you will need to pass as an string the range in A1 notation in your ArrayFormula, this is because the input argument only gives you the values in the cells, and with that extra argument it will be possible to obtain extra info. To make it work fine, always use the same range as the next example shows:
=ArrayFormula(dataParse('RAW DATA'!D2:D5, 2,"D2:D5"))
or
=ArrayFormula(dataParse('RAW DATA'!D2:D, 2,"D2:D"))
Notice I also added a new function called getDesiredRangeValue, which will take the values from the column you need, depending if one of the cells from Col D has the value google. This is how the function looks:
/*
// A1 (String): The col from where you will want the info
// range (String): It will be used to get the info in a range
// index (Integer): It gives the index number from the main array gotten in the input arg
*/
function getDesiredRangeValue(A1, range, index){
var rowNumbers = range.match(/\d+/g);
// It checks if the range will has and end or it will prolong without specifying and end row
if(rowNumbers.length > 1){
var rangeCol = ss.getRange(A1 + rowNumbers[0] + ":" + A1 + rowNumbers[1]).getValues();
} else {
var rangeCol = ss.getRange(A1 + rowNumbers[0] + ":" + A1).getValues();
}
// It returns the whole value from each cell in the specified col
return rangeCol[index][0];
}
Code
Now your whole code will look like this:
// Global var
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("RAW DATA");
function getSegment(data,index){
temp=data.split("_");
return temp[index-1];
}
/*
// A1 (String): The col from where you will want the info
// range (String): It will be used to get the info in a range
// index (Integer): It gives the index number from the main array gotten in the input arg
*/
function getDesiredRangeValue(A1, range, index){
var rowNumbers = range.match(/\d+/g);
// It checks if the range will has and end or it will prolong without specifying and end row
if(rowNumbers.length > 1){
var rangeCol = ss.getRange(A1 + rowNumbers[0] + ":" + A1 + rowNumbers[1]).getValues();
} else {
var rangeCol = ss.getRange(A1 + rowNumbers[0] + ":" + A1).getValues();
}
// It returns the whole value from each cell in the specified col
return rangeCol[index][0];
}
// range (String): It will be used to get the info in a range
function dataParse(input,dataSegment, range){
var val = "";
return Array.isArray(input) ? input.map(function(e, index){
return e.map(function(f){
if(f!=""){
// If col D has value google then take info from col B
if(f === "google") val = getDesiredRangeValue("B", range, index);
// else take info from col E
else val = getDesiredRangeValue("E", range, index);
// Take segment as needed
return getSegment(val,dataSegment);
}
}
)}
) : "false usage";
}
Docs
These are the docs I used to help you:
Class Sheet
Custom Functions
I would like to sync / update different calendars (bonus points for iCal but appreciate it will likely have to be Google Cal) for different clients from one google sheet
From this sheet:
https://docs.google.com/spreadsheets/d/1f6qUjGYHZtRRGCbyh5oNzM7o64B-wlkrTpB1ac64I2U/edit#gid=0
I would like info from columns C, D, E, F, G to input into a calendar entry / sync with respective calendars for clients A, B and C from column B according to the date in A
So for client A on date 06-01-2017 the following information would be added as an all day event:
Event: ABC
Location: ABC, DEF, GHI
Capacity: 5000
Seated Cap: 100
Link: www.google.com/abc
Client B on date 08-02-2017 the following would be added to Client B's calendar (but not Client A's etc)
Event: ABC
Location: ABC, DEF, GHI
Capacity: 2500
Seated Cap: 200
Link: www.google.com/abd
And as you can see there are multiple entries for the respective clients and their calendars
I have successfully got singular calendar working according to the following link but need an "if / then" specification and don't know where it goes or what code to use:
https://cloud.google.com/blog/products/g-suite/g-suite-pro-tip-how-to-automatically-add-a-schedule-from-google-sheets-into-calendar
You need to maintain a mapping of client to calendar ID somewhere so that each time you process an event row you can do:
var clientEventCal = CalendarApp.getCalendarById(clientCalendarId);
to make sure you're adding the event to the right calendar.
Note: If you share the Apps Script code you're using to create events to a single calendar using your spreadsheet structure, I can provide specific instructions on the exact changes to make!
For the sake of outlining a complete solution, let's assume you create a new sheet named "Client Calendars" containing Client or Project in column A and that client's Google Calendar ID in column B.
After you create this new client-to-client calendar ID table, you have a few options of how to use it:
Option A: Read the table directly in your code at runtime and look up the right Calendar ID to use in Apps Script code.
Option B: Add a new column to your main data sheet that uses each row's Client column value to populate the calendar ID directly onto your source data row using VLOOKUP.
(B) is probably easier, as it will require less Apps Script code modification.
A:
function createAllDayEvent(calendarId, eventName, date, location, description) {
var eventCal = CalendarApp.getCalendarById(calendarId);
var event = eventCal.createAllDayEvent(eventName, date, date, {location: location, description: description});
return event;
}
function createClientEvents(event) {
var sheet = event ? event.source.getSheets()[0] : SpreadsheetApp.getActiveSheet();
var startRow = 4;
var dataRange = sheet.getRange('A' + startRow.toString() + ':G');
var data = dataRange.getDisplayValues();
var clientCalendarSheet = ss.getSheetByName('Client Calendars');
var clientCalendarData = clientCalendarSheet.getDataRange().getValues();
// Date (Entry) | Client / Project | Event | Location | Capacity | Seated Cap | Link
var dateStr, client, eventName, location, capacity, seatedCap, link;
for (var i = startRow - 1; i < data.length; i++) {
[dateStr, client, eventName, location, capacity, seatedCap, link] = data[i];
if (!eventName) continue;
var calendarId = null;
for (var j = 0; j < clientCalendarData.length; j++) {
if (clientCalendarData[i][0] == client) {
calendarId = clientCalendarData[i][1];
break;
}
}
var date = new Date(dateStr);
var description = "Capacity: " + capacity + ", Seated Cap: " + seatedCap + ", Link: " + link;
createAllDayEvent(calendarId, eventName, date, location, description);
}
}
B:
This solution assumes data sheet column M contains client's Calendar ID (you could populate M using =IF($B4="",, VLOOKUP($B4, 'Client Calendars'!$A:$B, 2, FALSE)) assuming you've created the "Client Calendars" sheet as outlined above).
function createAllDayEvent(calendarId, eventName, date, location, description) {
var eventCal = CalendarApp.getCalendarById(calendarId);
var event = eventCal.createAllDayEvent(eventName, date, date, {location: location, description: description});
return event;
}
function createClientEvents(event) {
var sheet = event ? event.source.getSheets()[0] : SpreadsheetApp.getActiveSheet();
var startRow = 4;
var dataRange = sheet.getRange('A' + startRow.toString() + ':G');
var data = dataRange.getDisplayValues();
var calendarIdColumn = sheet.getRange('M' + startRow.toString() + ':M').getValues();
// Date (Entry) | Client / Project | Event | Location | Capacity | Seated Cap | Link
var dateStr, client, eventName, location, capacity, seatedCap, link;
for (var i = startRow - 1; i < data.length; i++) {
[dateStr, client, eventName, location, capacity, seatedCap, link] = data[i];
if (!eventName) continue;
var calendarId = calendarIdColumn[i][0];
var date = new Date(dateStr);
var description = "Capacity: " + capacity + ", Seated Cap: " + seatedCap + ", Link: " + link;
createAllDayEvent(calendarId, eventName, date, location, description);
}
}
I currently have a filter working on a fusion table rendered as a Map Layer, and I want to zoom to best fit all of the data whenever the filter is changed.
I figure I need to wait until the query is applied and then iterate through the markers to find the min/max x & y locations and pan to that rectangle, but I don't see a way in the Maps api to access the markers of a layer.
Anyone have an idea how to do this?
The short answer is no. To me this is one of the shortcomings of dealing with Fusion Tables via the Maps API. E.g. wanting to display a count of the results of my most recent query. But there is a work-around through the "undocumented" JSONP API to Fusion Tables. I've had great success using it but I must credit Robin Kraft with informing me about this API.
http://www.reddmetrics.com/2011/08/10/fusion-tables-javascript-query-maps.html.
Here's some code which allows you to re-execute your most recent query via an AJAX JSONP request and do what you want with the results, such as calculating the bounding-box. Note: this example uses Jquery for the AJAX JSONP calls. This example creates a <table> display but can be modified as needed.
<script src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
// Example call
getFTData(tableid, 'latitude,longitude', example_dataHandler);
<script>
// Globals same for all requests
var queryUrlHead = 'https://fusiontables.googleusercontent.com/fusiontables/api/query?sql=';
var queryUrlTail = '&jsonCallback=?'; // ? could be a function name
// getFTData()
// table_id - Fusion Table id MUST have public permissions
// col_list - comma separated list of FT column names
// successFunction - function to parse the CSV results (see exampleParser below)
//////////////////////////////
function getFTData(table_id, col_list, successFunction) {
var query = "SELECT " + col_list + " FROM " + table_id;
var queryurl = encodeURI(queryUrlHead + query + queryUrlTail);
$.ajax({
type: "GET",
url: queryurl,
dataType: "jsonp", // return CSV FustionTable response as JSON
success: successFunction,
error: function () {alert("AJAX ERROR for " + queryurl ); }
});
}
function example_dataHandler(d) {
// get the actual data out of the JSON object
var cols = d.table.cols;
var rows = d.table.rows;
var row_count = 0;
var results = '<table border="1" cellpadding="4">';
results += '<tr>';
for (var i = 0; i < cols.length; i++) {
results += '<th>' + cols[i] + '</th>';
}
results += '</tr>';
// loop through all rows to add them to the map
for (var i = 0; i < rows.length; i++) {
// Per the expected columns
results += '<tr>';
for(j=0; j < rows[i].length; j++)
{
results += '<td>' + rows[i][j] + '</td>';
}
results += '</tr>';
row_count++;
}
results += '</table>';
results += '<br />';
results += 'Row Count: ' + row_count + '<br />';;
document.getElementById("program_select").innerHTML = results;
}
</script>
Since retrieving the count of recent Fusion Table rows returned is common, I'm adding a snippet of how to do that.
<script src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
<script type="text/javascript">
var tableid = 3167783
var where = "WHERE type = 9";
getFTCount(current_table_id, where, displayCount);
// Globals same for all request
var queryUrlHead = 'https://fusiontables.googleusercontent.com/fusiontables/api/query?sql=';
var queryUrlTail = '&jsonCallback=?'; // ? could be a function name
///////////////////////////////
// Get Counts from Fusion Tables.
// table_id required
// where optional "WHERE column == 'value' " where clause for count()
// successFunction callback required
///////////////////////////////
function getFTCount(table_id, where, successFunction) {
if(!table_id){
alert("table_id required.");
return;
}
if(!successFunction){
alert("successFunction callback required.");
return;
}
var query = "SELECT count() FROM " + table_id;
if(where){
query += ' ' + where;
}
var queryurl = encodeURI(queryUrlHead + query + queryUrlTail);
$.ajax({
type: "GET",
url: queryurl,
dataType: "jsonp", // return CSV FustionTable response as JSON
success: successFunction,
error: function () {alert("AJAX ERROR for " + queryurl ); }
});
}
function displayCount(d) {
var count = d.table.rows[0];
alert(count);
}
</script>
If your data is in a fusion table, then use the fusion table's sql api to find the Max/Min val for Lat and Lng respectively:
https://www.googleapis.com/fusiontables/v1/query?sql=SELECT
MINIMUM(Lat) AS MinLat, MAXIMUM(Lat) AS MaxLat,
MINIMUM(Long) AS MinLong, MAXIMUM(Long) AS MaxLong
FROM <table_id>
See here for full details on api: https://developers.google.com/fusiontables/docs/v1/sql-reference. (One thing to remember is to ecodeURI this sql statement)
This returns those for value to json array. And as I'm sure your aware, use these values to set your map's 'center' and 'zoom' parameters.