package edu.stanford.nlp.sempre.freebase;
import edu.stanford.nlp.sempre.*;
import edu.stanford.nlp.sempre.cache.StringCache;
import edu.stanford.nlp.sempre.cache.StringCacheUtils;
import fig.basic.*;
import java.io.*;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.*;
/**
* Takes a string (e.g., "obama") and asks the Freebase Search API.
* Caches if necessary.
* Outputs a set of entities.
*
* @author Percy Liang
*/
public class FreebaseSearch {
public static class Entry {
public Entry(String mid, String id, String name, double score) {
this.mid = mid;
this.id = id;
this.name = name;
this.score = score;
}
public final String mid;
public final String id;
public final String name;
public final double score;
public LispTree toLispTree() {
LispTree tree = LispTree.proto.newList();
tree.addChild(mid);
tree.addChild(id == null ? "" : id);
tree.addChild(name);
tree.addChild(score + "");
return tree;
}
public String toString() { return toLispTree().toString(); }
}
public static class Options {
@Option(gloss = "Milliseconds to wait until opening connection times out")
public int connectTimeoutMs = 1 * 60 * 1000;
@Option(gloss = "Milliseconds to wait until reading connection times out")
public int readTimeoutMs = 1 * 60 * 1000;
@Option(gloss = "API key (needed to get more access)")
public String apiKey;
@Option(gloss = "Save results of Freebase API search")
public String cachePath;
}
public static Options opts = new Options();
private final StringCache cache;
public class ServerResponse {
public ServerResponse() { this.entries = new ArrayList<>(); this.error = null; }
public ServerResponse(ErrorValue error) { this.entries = null; this.error = error; }
public final List<Entry> entries;
public final ErrorValue error;
boolean cached;
long timeMs;
}
public FreebaseSearch() {
if (opts.cachePath != null)
this.cache = StringCacheUtils.create(opts.cachePath);
else
this.cache = null;
}
@SuppressWarnings("unchecked")
public ServerResponse lookup(String query) {
StopWatch watch = new StopWatch();
watch.start();
String output = null;
ServerResponse response = new ServerResponse();
// First, try the cache.
if (cache != null) {
output = cache.get(query);
if (output != null) response.cached = true;
}
// If got nothing, then need to hit the server.
if (output == null) {
try {
// Setup the connection
String url = String.format("https://www.googleapis.com/freebase/v1/search?query=%s", URLEncoder.encode(query, "UTF-8"));
if (opts.apiKey != null) url += "&key=" + opts.apiKey;
URLConnection conn = new URL(url).openConnection();
conn.setConnectTimeout(opts.connectTimeoutMs);
conn.setReadTimeout(opts.readTimeoutMs);
InputStream in = conn.getInputStream();
// Read the response
StringBuilder buf = new StringBuilder();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String line;
while ((line = reader.readLine()) != null)
buf.append(line);
reader.close();
// Put the result in the cache
output = buf.toString();
if (cache != null)
cache.put(query, output);
} catch (SocketTimeoutException e) {
return new ServerResponse(ErrorValue.timeout);
} catch (IOException e) {
LogInfo.errors("Server exception: %s", e);
if (e.toString().contains("HTTP response code: 408"))
return new ServerResponse(ErrorValue.server408);
if (e.toString().contains("HTTP response code: 500"))
return new ServerResponse(ErrorValue.server500);
throw new RuntimeException(e); // Haven't seen this happen yet...
}
}
// Parse the result
Map<String, Object> results = Json.readMapHard(output);
for (Object resultObj : (List) results.get("result")) {
Map<String, Object> result = (Map<String, Object>) resultObj;
String mid = (String) result.get("mid");
String id = (String) result.get("id");
mid = toRDF(mid);
id = toRDF(id);
String name = (String) result.get("name");
double score = (double) result.get("score");
response.entries.add(new Entry(mid, id, name, score));
}
watch.stop();
response.timeMs = watch.getCurrTimeLong();
LogInfo.logs("FreebaseSearch %s => %s results (cached=%s)", query, response.entries.size(), response.cached);
return response;
}
// /en/barack_obama => fb:en.barack_obama
private String toRDF(String s) {
if (s == null) return s;
return "fb:" + s.substring(1).replaceAll("/", ".");
}
public static void main(String[] args) {
opts.cachePath = "FreebaseSearch.cache";
String query = StrUtils.join(args, " ");
query = "obama";
FreebaseSearch search = new FreebaseSearch();
LogInfo.logs("%s", search.lookup(query).entries);
}
}