Skip to content
This repository has been archived by the owner on Mar 7, 2018. It is now read-only.

Commit

Permalink
Merge pull request #102 from CatalystCode/fix-tiles-queries
Browse files Browse the repository at this point in the history
Fix tiles queries for cluster cassandra
  • Loading branch information
c-w committed Aug 16, 2017
2 parents 8d68b85 + 872317a commit ef9ddc5
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 60 deletions.
173 changes: 115 additions & 58 deletions src/resolvers-cassandra/Tiles/queries.js
Expand Up @@ -4,7 +4,7 @@ const Promise = require('promise');
const geotile = require('geotile');
const cassandraConnector = require('../../clients/cassandra/CassandraConnector');
const featureServiceClient = require('../../clients/locations/FeatureServiceClient');
const { tilesForBbox, parseFromToDate, withRunTime, toConjunctionTopics, toPipelineKey } = require('../shared');
const { tilesForBbox, tilesForLocations, parseFromToDate, withRunTime, toConjunctionTopics, toPipelineKey } = require('../shared');
const { trackEvent } = require('../../clients/appinsights/AppInsightsClient');
const { makeSet } = require('../../utils/collections');

Expand All @@ -25,21 +25,9 @@ function computedtileToTile(row) {
};
}

/**
* @param {{site: string, bbox: number[], mainEdge: string, filteredEdges: string[], zoomLevel: number, sourceFilter: string[], fromDate: string, toDate: string, originalSource: string}} args
* @returns {Promise.<{runTime: string, type: string, bbox: number[], features: Array<{type: string, coordinates: number[], properties: {mentionCount: number, location: string, population: number, neg_sentiment: number, pos_sentiment: number, tileId: string}}>}>}
*/
function fetchTilesByBBox(args, res) { // eslint-disable-line no-unused-vars
function queryComputedTiles(tiles, args) {
return new Promise((resolve, reject) => {
if (!args || !args.bbox) return reject('No bounding box for which to fetch tiles specified');
if (!args || args.zoomLevel == null) return reject('No zoom level for which to fetch tiles specified');
if (!args || !args.mainEdge) return reject('No main edge for keyword filter specified');
if (!args || !args.filteredEdges) return reject('No secondary edges for keyword filter specified');
if (!args || !args.fromDate || !args.toDate) return reject('No time period for which to fetch edges specified');
if (args.bbox.length !== 4) return reject('Invalid bounding box for which to fetch tiles specified');

const { periodType, period, fromDate, toDate } = parseFromToDate(args.fromDate, args.toDate);
const tiles = tilesForBbox(args.bbox, args.zoomLevel);
const tilex = makeSet(tiles, tile => tile.row);
const tiley = makeSet(tiles, tile => tile.column);

Expand Down Expand Up @@ -77,9 +65,84 @@ function fetchTilesByBBox(args, res) { // eslint-disable-line no-unused-vars

cassandraConnector.executeQuery(query, params)
.then(rows => {
const features = rows.map(computedtileToTile);

resolve(features);
})
.catch(reject);
});
}

function queryPopularTopics(tiles, args) {
return new Promise((resolve, reject) => {
const { periodType, period, fromDate, toDate } = parseFromToDate(args.fromDate, args.toDate);
const tilex = makeSet(tiles, tile => tile.row);
const tiley = makeSet(tiles, tile => tile.column);

const query = `
SELECT mentionCount, topic
FROM fortis.populartopics
WHERE periodtype = ?
AND pipelinekey = ?
AND externalsourceid = ?
AND tilez = ?
AND topic = ?
AND period = ?
AND (tilex, tiley, periodstartdate, periodenddate) <= (?, ?, ?, ?)
AND (tilex, tiley, periodstartdate, periodenddate) >= (?, ?, ?, ?)
`.trim();

const params = [
periodType,
toPipelineKey(args.sourceFilter),
args.originalSource || 'all',
args.zoomLevel,
args.mainEdge,
period,
Math.max(...tilex),
Math.max(...tiley),
toDate,
toDate,
Math.min(...tilex),
Math.min(...tiley),
fromDate,
fromDate
];

cassandraConnector.executeQuery(query, params)
.then(rows => {
const edges = rows.map(row => ({
mentionCount: row.mentionCount,
name: row.topic
}));

resolve(edges);
})
.catch(reject);
});
}

/**
* @param {{site: string, bbox: number[], mainEdge: string, filteredEdges: string[], zoomLevel: number, sourceFilter: string[], fromDate: string, toDate: string, originalSource: string}} args
* @returns {Promise.<{runTime: string, type: string, bbox: number[], features: Array<{type: string, coordinates: number[], properties: {mentionCount: number, location: string, population: number, neg_sentiment: number, pos_sentiment: number, tileId: string}}>}>}
*/
function fetchTilesByBBox(args, res) { // eslint-disable-line no-unused-vars
return new Promise((resolve, reject) => {
if (!args || !args.bbox) return reject('No bounding box for which to fetch tiles specified');
if (!args || args.zoomLevel == null) return reject('No zoom level for which to fetch tiles specified');
if (!args || !args.mainEdge) return reject('No main edge for keyword filter specified');
if (!args || !args.filteredEdges) return reject('No secondary edges for keyword filter specified');
if (!args || !args.fromDate || !args.toDate) return reject('No time period for which to fetch edges specified');
if (args.bbox.length !== 4) return reject('Invalid bounding box for which to fetch tiles specified');

const tiles = tilesForBbox(args.bbox, args.zoomLevel);
queryComputedTiles(tiles, args)
.then((features) => {
const bbox = args.bbox;

resolve({
bbox: args.bbox,
features: rows.map(computedtileToTile)
features,
bbox
});
})
.catch(reject);
Expand All @@ -92,7 +155,21 @@ function fetchTilesByBBox(args, res) { // eslint-disable-line no-unused-vars
*/
function fetchTilesByLocations(args, res) { // eslint-disable-line no-unused-vars
return new Promise((resolve, reject) => {
return reject('Querying by location is no longer supported, please query using the place name instead');
if (!args || !args.locations || !args.locations.length) return reject('No locations for which to fetch tiles specified');
if (!args || args.zoomLevel == null) return reject('No zoom level for which to fetch tiles specified');
if (!args || !args.mainEdge) return reject('No main edge for keyword filter specified');
if (!args || !args.filteredEdges) return reject('No secondary edges for keyword filter specified');
if (!args || !args.fromDate || !args.toDate) return reject('No time period for which to fetch edges specified');

const tiles = tilesForLocations(args.locations, args.zoomLevel);
queryComputedTiles(tiles, args)
.then((features) => {

resolve({
features
});
})
.catch(reject);
});
}

Expand All @@ -108,9 +185,11 @@ function fetchPlacesByBBox(args, res) { // eslint-disable-line no-unused-vars
featureServiceClient.fetchByBbox({north: args.bbox[0], west: args.bbox[1], south: args.bbox[2], east: args.bbox[3]})
.then(places => {
const features = places.map(place => ({coordinate: place.bbox, name: place.name, id: place.id}));
const bbox = args.bbox;

resolve({
features: features,
bbox: args.bbox
features,
bbox
});
})
.catch(reject);
Expand All @@ -123,7 +202,19 @@ function fetchPlacesByBBox(args, res) { // eslint-disable-line no-unused-vars
*/
function fetchEdgesByLocations(args, res) { // eslint-disable-line no-unused-vars
return new Promise((resolve, reject) => {
return reject('Querying by location is no longer supported, please query using the place name instead');
if (!args || !args.mainEdge) return reject('No main edge for which to fetch edges specified');
if (!args || args.zoomLevel == null) return reject('No zoom level for which to fetch edges specified');
if (!args || !args.fromDate || !args.toDate) return reject('No time period for which to fetch edges specified');
if (!args || !args.locations || !args.locations.length) return reject('No locations for which to fetch edges specified');

const tiles = tilesForLocations(args.locations, args.zoomLevel);
queryPopularTopics(tiles, args)
.then((edges) => {
resolve({
edges
});
})
.catch(reject);
});
}

Expand All @@ -134,50 +225,16 @@ function fetchEdgesByLocations(args, res) { // eslint-disable-line no-unused-var
function fetchEdgesByBBox(args, res) { // eslint-disable-line no-unused-vars
return new Promise((resolve, reject) => {
if (!args || !args.mainEdge) return reject('No main edge for which to fetch edges specified');
if (!args || !args.bbox) return reject('No bounding box for which to fetch edges specified');
if (!args || args.zoomLevel == null) return reject('No zoom level for which to fetch edges specified');
if (!args || !args.fromDate || !args.toDate) return reject('No time period for which to fetch edges specified');
if (!args || !args.bbox) return reject('No bounding box for which to fetch edges specified');
if (args.bbox.length !== 4) return reject('Invalid bounding box for which to fetch edges specified');

const { periodType, period, fromDate, toDate } = parseFromToDate(args.fromDate, args.toDate);
const tiles = tilesForBbox(args.bbox, args.zoomLevel);
const tilex = makeSet(tiles, tile => tile.row);
const tiley = makeSet(tiles, tile => tile.column);

const query = `
SELECT mentionCount, topic
FROM fortis.populartopics
WHERE periodtype = ?
AND pipelinekey = ?
AND externalsourceid = ?
AND tilez = ?
AND topic = ?
AND period = ?
AND (tilex, tiley, periodstartdate, periodenddate) <= (?, ?, ?, ?)
AND (tilex, tiley, periodstartdate, periodenddate) >= (?, ?, ?, ?)
`.trim();

const params = [
periodType,
toPipelineKey(args.sourceFilter),
args.originalSource || 'all',
args.zoomLevel,
args.mainEdge,
period,
Math.max(...tilex),
Math.max(...tiley),
toDate,
toDate,
Math.min(...tilex),
Math.min(...tiley),
fromDate,
fromDate
];

cassandraConnector.executeQuery(query, params)
.then(rows => {
queryPopularTopics(tiles, args)
.then((edges) => {
resolve({
edges: rows.map(row => ({mentionCount: row.mentionCount, name: row.topic}))
edges
});
})
.catch(reject);
Expand Down
5 changes: 5 additions & 0 deletions src/resolvers-cassandra/shared.js
Expand Up @@ -67,6 +67,10 @@ function tilesForBbox(bbox, zoomLevel) {
return geotile.tileIdsForBoundingBox(fence, zoomLevel).map(geotile.decodeTileId);
}

function tilesForLocations(locations, zoomLevel) {
return locations.map(([lat, lon]) => geotile.tileIdFromLatLong(lat, lon, zoomLevel)).map(geotile.decodeTileId);
}

function parseTimespan(timespan) {
// TODO: implement
return {
Expand Down Expand Up @@ -96,6 +100,7 @@ module.exports = {
toPipelineKey,
toConjunctionTopics,
tilesForBbox,
tilesForLocations,
limitForInClause,
withRunTime: withRunTime
};
4 changes: 2 additions & 2 deletions src/schemas/TilesSchema.js
Expand Up @@ -4,8 +4,8 @@ module.exports = graphql.buildSchema(`
type Query {
fetchTilesByBBox(site: String!, bbox: [Float]!, mainEdge: String!, filteredEdges: [String], timespan: String!, zoomLevel: Int, sourceFilter: [String], fromDate: String, toDate: String, originalSource: String): FeatureCollection,
fetchPlacesByBBox(site: String!, bbox: [Float]!, zoom: Int): PlaceCollection,
fetchTilesByLocations(site: String!, locations: [[Float]]!, filteredEdges: [String], timespan: String!, sourceFilter: [String], fromDate: String, toDate: String): FeatureCollection,
fetchEdgesByLocations(site: String!, locations: [[Float]]!, timespan: String!, sourceFilter: [String], fromDate: String, toDate: String): EdgeCollection,
fetchTilesByLocations(site: String!, locations: [[Float]]!, mainEdge: String, zoomLevel: Int, filteredEdges: [String], timespan: String!, sourceFilter: [String], fromDate: String, toDate: String): FeatureCollection,
fetchEdgesByLocations(site: String!, locations: [[Float]]!, zoomLevel: Int, mainEdge: String, filteredEdges: [String], timespan: String!, sourceFilter: [String], fromDate: String, toDate: String): EdgeCollection,
fetchEdgesByBBox(site: String!, bbox: [Float]!, zoomLevel: Int, mainEdge: String!, timespan: String!, sourceFilter: [String], fromDate: String, toDate: String, originalSource: String): EdgeCollection
}
Expand Down

0 comments on commit ef9ddc5

Please sign in to comment.