package edu.stanford.nlp.sempre.freebase; import fig.basic.IOUtils; import fig.basic.LogInfo; import fig.basic.Option; import fig.exec.Execution; import java.io.BufferedReader; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * Motivation: the Freebase dump uses a mix of mids and ids. The mapping * computed by this program will be used to standardize everything to a nice * looking id or a mid. * <p/> * Input: raw RDF Freebase dump (ttl format) downloaded from the Freebase * website. * <p/> * Output: tab-separated file containing mapping from mid/id to canonical id. * fb:m.02mjmr fb:en.barack_obama fb:m.07t65 fb:en.united_nations * fb:en.united_staff fb:en.united_nations ... To save space, only output mid/id * which are mids or acceptable ids. * <p/> * Acceptable ids include: - Something that starts with fb:en. - Something that * occurs in the arg1 position (for types and properties, excluding fb:m.* and * fb:g.*). If there are multiple ids, just take the first one in the file. If * there are no acceptable ids, then just take the mid. * * @author Percy Liang */ public class BuildCanonicalIdMap implements Runnable { @Option public int maxInputLines = Integer.MAX_VALUE; @Option(required = true) public String rawPath; @Option(required = true) public String canonicalIdMapPath; Set<String> allowableIds = new HashSet<String>(); PrintWriter out; int numMids = 0; void computeAllowableIds() { // Compute allowable ids LogInfo.begin_track("Compute allowable ids"); try { BufferedReader in = IOUtils.openIn(rawPath); String line; int numInputLines = 0; while (numInputLines < maxInputLines && (line = in.readLine()) != null) { numInputLines++; if (numInputLines % 10000000 == 0) LogInfo.logs("Read %s lines, %d allowable ids", numInputLines, allowableIds.size()); String[] tokens = Utils.parseTriple(line); if (tokens == null) continue; String arg1 = tokens[0]; if (!arg1.startsWith("fb:g.") && !arg1.startsWith("fb:m.")) allowableIds.add(arg1); } LogInfo.logs("%d allowable ids", allowableIds.size()); in.close(); } catch (IOException e) { throw new RuntimeException(e); } LogInfo.end_track(); } void flush(String mid, List<String> ids) { if (ids.size() == 0) return; numMids++; // Find the best id for this entity and put it first to use as the canonical id. String bestId = mid; for (String id : ids) { if (id.startsWith("fb:en.") || allowableIds.contains(id)) { bestId = id; break; } } if (!bestId.equals(mid)) out.println(mid + "\t" + bestId); for (String id : ids) { if (id.equals(bestId)) continue; if (!(allowableIds.contains(id) || id.startsWith("fb:en."))) continue; // To save space, only map ids which look reasonable out.println(id + "\t" + bestId); } } public void run() { computeAllowableIds(); // Map to ids out = IOUtils.openOutHard(canonicalIdMapPath); try { BufferedReader in = IOUtils.openIn(rawPath); String line; int numInputLines = 0; // Current block of triples corresponds to a single mid. String mid = null; List<String> ids = new ArrayList<String>(); while (numInputLines < maxInputLines && (line = in.readLine()) != null) { numInputLines++; if (numInputLines % 10000000 == 0) LogInfo.logs("Read %s lines, %d entities", numInputLines, numMids); String[] tokens = Utils.parseTriple(line); if (tokens == null) continue; String arg1 = tokens[0]; String property = tokens[1]; String arg2 = tokens[2]; if (!arg1.startsWith("fb:m.")) continue; if (!property.equals("fb:type.object.key")) continue; // Flush last block if (!arg1.equals(mid)) { flush(mid, ids); // Reset mid = arg1; ids.clear(); } // Record information arg2 = Utils.stringToRdf(arg2); ids.add(arg2); } in.close(); flush(mid, ids); } catch (IOException e) { throw new RuntimeException(e); } out.close(); } public static void main(String[] args) { Execution.run(args, new BuildCanonicalIdMap()); } }