Twitter River: Support filter stream, closes #416.
This commit is contained in:
parent
ee2fabb9dd
commit
8b03b914f9
|
@ -45,6 +45,7 @@
|
||||||
<w>encodable</w>
|
<w>encodable</w>
|
||||||
<w>estab</w>
|
<w>estab</w>
|
||||||
<w>failover</w>
|
<w>failover</w>
|
||||||
|
<w>firehose</w>
|
||||||
<w>flushable</w>
|
<w>flushable</w>
|
||||||
<w>formatter</w>
|
<w>formatter</w>
|
||||||
<w>formatters</w>
|
<w>formatters</w>
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<orderEntry type="module-library">
|
<orderEntry type="module-library">
|
||||||
<library name="twitter4j">
|
<library name="twitter4j">
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$GRADLE_REPOSITORY$/org.twitter4j/twitter4j-core/jars/twitter4j-core-2.1.4.jar!/" />
|
<root url="jar://$GRADLE_REPOSITORY$/org.twitter4j/twitter4j-core/jars/twitter4j-core-2.1.6.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
<SOURCES />
|
<SOURCES />
|
||||||
|
|
|
@ -32,8 +32,8 @@ configurations {
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':elasticsearch')
|
compile project(':elasticsearch')
|
||||||
|
|
||||||
compile('org.twitter4j:twitter4j-core:2.1.4') { transitive = false }
|
compile('org.twitter4j:twitter4j-core:2.1.6') { transitive = false }
|
||||||
distLib('org.twitter4j:twitter4j-core:2.1.4') { transitive = false }
|
distLib('org.twitter4j:twitter4j-core:2.1.6') { transitive = false }
|
||||||
|
|
||||||
|
|
||||||
testCompile project(':test-testng')
|
testCompile project(':test-testng')
|
||||||
|
|
|
@ -26,6 +26,7 @@ import org.elasticsearch.client.Client;
|
||||||
import org.elasticsearch.client.Requests;
|
import org.elasticsearch.client.Requests;
|
||||||
import org.elasticsearch.client.action.bulk.BulkRequestBuilder;
|
import org.elasticsearch.client.action.bulk.BulkRequestBuilder;
|
||||||
import org.elasticsearch.cluster.block.ClusterBlockException;
|
import org.elasticsearch.cluster.block.ClusterBlockException;
|
||||||
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.inject.Inject;
|
import org.elasticsearch.common.inject.Inject;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||||
|
@ -38,6 +39,7 @@ import org.elasticsearch.river.RiverSettings;
|
||||||
import twitter4j.*;
|
import twitter4j.*;
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
@ -56,6 +58,10 @@ public class TwitterRiver extends AbstractRiverComponent implements River {
|
||||||
|
|
||||||
private final int dropThreshold;
|
private final int dropThreshold;
|
||||||
|
|
||||||
|
private FilterQuery filterQuery;
|
||||||
|
|
||||||
|
private String streamType;
|
||||||
|
|
||||||
|
|
||||||
private final TwitterStream stream;
|
private final TwitterStream stream;
|
||||||
|
|
||||||
|
@ -73,6 +79,84 @@ public class TwitterRiver extends AbstractRiverComponent implements River {
|
||||||
Map<String, Object> twitterSettings = (Map<String, Object>) settings.settings().get("twitter");
|
Map<String, Object> twitterSettings = (Map<String, Object>) settings.settings().get("twitter");
|
||||||
user = XContentMapValues.nodeStringValue(twitterSettings.get("user"), null);
|
user = XContentMapValues.nodeStringValue(twitterSettings.get("user"), null);
|
||||||
password = XContentMapValues.nodeStringValue(twitterSettings.get("password"), null);
|
password = XContentMapValues.nodeStringValue(twitterSettings.get("password"), null);
|
||||||
|
streamType = XContentMapValues.nodeStringValue(twitterSettings.get("type"), "sample");
|
||||||
|
Map<String, Object> filterSettings = (Map<String, Object>) twitterSettings.get("filter");
|
||||||
|
if (filterSettings != null) {
|
||||||
|
filterQuery = new FilterQuery();
|
||||||
|
filterQuery.count(XContentMapValues.nodeIntegerValue(filterSettings.get("count"), 0));
|
||||||
|
Object tracks = filterSettings.get("tracks");
|
||||||
|
if (tracks != null) {
|
||||||
|
if (tracks instanceof List) {
|
||||||
|
List<String> lTracks = (List<String>) tracks;
|
||||||
|
filterQuery.track(lTracks.toArray(new String[lTracks.size()]));
|
||||||
|
} else {
|
||||||
|
filterQuery.track(Strings.commaDelimitedListToStringArray(tracks.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Object follow = filterSettings.get("follow");
|
||||||
|
if (follow != null) {
|
||||||
|
if (follow instanceof List) {
|
||||||
|
List lFollow = (List) follow;
|
||||||
|
int[] followIds = new int[lFollow.size()];
|
||||||
|
for (int i = 0; i < lFollow.size(); i++) {
|
||||||
|
Object o = lFollow.get(i);
|
||||||
|
if (o instanceof Number) {
|
||||||
|
followIds[i] = ((Number) o).intValue();
|
||||||
|
} else {
|
||||||
|
followIds[i] = Integer.parseInt(o.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filterQuery.follow(followIds);
|
||||||
|
} else {
|
||||||
|
String[] ids = Strings.commaDelimitedListToStringArray(follow.toString());
|
||||||
|
int[] followIds = new int[ids.length];
|
||||||
|
for (int i = 0; i < ids.length; i++) {
|
||||||
|
followIds[i] = Integer.parseInt(ids[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Object locations = filterSettings.get("locations");
|
||||||
|
if (locations != null) {
|
||||||
|
if (locations instanceof List) {
|
||||||
|
List lLocations = (List) locations;
|
||||||
|
double[][] dLocations = new double[lLocations.size()][];
|
||||||
|
for (int i = 0; i < lLocations.size(); i++) {
|
||||||
|
Object loc = lLocations.get(i);
|
||||||
|
double lat;
|
||||||
|
double lon;
|
||||||
|
if (loc instanceof List) {
|
||||||
|
List lLoc = (List) loc;
|
||||||
|
if (lLoc.get(0) instanceof Number) {
|
||||||
|
lat = ((Number) lLoc.get(0)).doubleValue();
|
||||||
|
} else {
|
||||||
|
lat = Double.parseDouble(lLoc.get(0).toString());
|
||||||
|
}
|
||||||
|
if (lLoc.get(1) instanceof Number) {
|
||||||
|
lon = ((Number) lLoc.get(1)).doubleValue();
|
||||||
|
} else {
|
||||||
|
lon = Double.parseDouble(lLoc.get(1).toString());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String[] sLoc = Strings.commaDelimitedListToStringArray(loc.toString());
|
||||||
|
lat = Double.parseDouble(sLoc[0]);
|
||||||
|
lon = Double.parseDouble(sLoc[1]);
|
||||||
|
}
|
||||||
|
dLocations[i] = new double[]{lat, lon};
|
||||||
|
}
|
||||||
|
filterQuery.locations(dLocations);
|
||||||
|
} else {
|
||||||
|
String[] sLocations = Strings.commaDelimitedListToStringArray(locations.toString());
|
||||||
|
double[][] dLocations = new double[sLocations.length / 2][];
|
||||||
|
int dCounter = 0;
|
||||||
|
for (int i = 0; i < sLocations.length; i++) {
|
||||||
|
double lat = Double.parseDouble(sLocations[i]);
|
||||||
|
double lon = Double.parseDouble(sLocations[++i]);
|
||||||
|
dLocations[dCounter++] = new double[]{lat, lon};
|
||||||
|
}
|
||||||
|
filterQuery.locations(dLocations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("creating twitter stream river for [{}]", user);
|
logger.info("creating twitter stream river for [{}]", user);
|
||||||
|
@ -80,7 +164,7 @@ public class TwitterRiver extends AbstractRiverComponent implements River {
|
||||||
if (user == null || password == null) {
|
if (user == null || password == null) {
|
||||||
stream = null;
|
stream = null;
|
||||||
indexName = null;
|
indexName = null;
|
||||||
typeName = null;
|
typeName = "status";
|
||||||
bulkSize = 100;
|
bulkSize = 100;
|
||||||
dropThreshold = 10;
|
dropThreshold = 10;
|
||||||
logger.warn("no user / password specified, disabling river...");
|
logger.warn("no user / password specified, disabling river...");
|
||||||
|
@ -104,6 +188,9 @@ public class TwitterRiver extends AbstractRiverComponent implements River {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void start() {
|
@Override public void start() {
|
||||||
|
if (stream == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
logger.info("starting twitter stream");
|
logger.info("starting twitter stream");
|
||||||
try {
|
try {
|
||||||
String mapping = XContentFactory.jsonBuilder().startObject().startObject(typeName)
|
String mapping = XContentFactory.jsonBuilder().startObject().startObject(typeName)
|
||||||
|
@ -122,8 +209,18 @@ public class TwitterRiver extends AbstractRiverComponent implements River {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
currentRequest = client.prepareBulk();
|
currentRequest = client.prepareBulk();
|
||||||
|
if (streamType.equals("filter") || filterQuery != null) {
|
||||||
|
try {
|
||||||
|
stream.filter(filterQuery);
|
||||||
|
} catch (TwitterException e) {
|
||||||
|
logger.warn("failed to create filter stream based on query, disabling river....");
|
||||||
|
}
|
||||||
|
} else if (streamType.equals("firehose")) {
|
||||||
|
stream.firehose(0);
|
||||||
|
} else {
|
||||||
stream.sample();
|
stream.sample();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override public void close() {
|
@Override public void close() {
|
||||||
logger.info("closing twitter stream river");
|
logger.info("closing twitter stream river");
|
||||||
|
|
Loading…
Reference in New Issue