Home > Freebase, Java, JPA, Structured Data > Working with Freebase – Part 3

Working with Freebase – Part 3

My next step with Freebase was to generalize the way I queried it. I decided to model my API after the Java Persistence API (JPA) which provides a POJO persistence model for object-relational mapping. In this case its an object-structured data mapping. The Freebase graph is a set of nodes and a set of links or relationships between those nodes. My API will provide a straight forward way to retrieve those nodes and relationships in the form of a POJO. This post will describe my initial efforts.

In its most primitive form a Freebase node has a unique identifier, creation time stamp, and creator. Properties specify a relationship. That can be a relationship with another node or primitive value. Unique properties have only one relationship to a node/value. Non-unique properties can have many.  There are also universal properties (name, key, type, permission, mid). Primitive values include int, float, boolean, text, rawstring, uri, datetime, key, and id.

JPA describes an entity as …

An entity is a lightweight persistence domain object. Typically an entity represents a table in a relational database, and each entity instance corresponds to a row in that table. The primary programming artifact of an entity is the entity class, although entities can use helper classes.

The persistent state of an entity is represented either through persistent fields or persistent properties. These fields or properties use object/relational mapping annotations to map the entities and entity relationships to the relational data in the underlying data store.

I will reuse this concept of entity to represent a Freebase node. For example, the following class represents a node of type /people/person.

@FBEntity(type = "/people/person")
public class Person implements Serializable {

	@FBProperty(property_value = "/people/person")
	private String type;
	@FBProperty
	private String id;
	@FBProperty
	private String name;

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

Consider the simplest of queries – select all from. In SQL this would look like SELECT * FROM name. MQL does not have a true equivalent.  The following MQL would select type, id, name from node type /people/person.

[{
 "type": "/people/person",
 "id": null,
 "name": null
}]

The code for executing this query looks like this.


public class QueryTestOne {
	private static final String personQuery =
			"[{"						+
				  "\"type\": \"/people/person\","	+
				  "\"id\": null,"			+
				  "\"name\": null"			+
				  "}]";

	public static  void dump(List list) {
		if (list != null) {
			for (T result : list) {
				System.out.printf("result: %s\n", result);
			}
		}
	}

	public static void test1(EntityManager em, String query) {
		Query q = em.createQuery(query);
		List<?> list = q.getResultList();
		dump(list);
	}

	public static void test2(EntityManager em, String query, Class<?> clazz) {
		Query q = em.createQuery(query, clazz);
		List<?> list = q.getResultList();
		dump(list);
	}

	public static void main(String[] args) {
		EntityManagerFactory emf = Persistence.createEntityManagerFactory("https", "www.googleapis.com", "/freebase/v1/mqlread/", null);
		EntityManager em = emf.createEntityManager();

		test1(em, personQuery);
		test2(em, personQuery, Person.class);
	}
}

The Freebase read API will return the results in a JSON response envelop that uses the following general form.

{
  "result": [{ MQL Response Here }],
  "status": "200 OK",
  "code": "/api/status/ok",
  "transaction_id":[opaque string value]
}

result contains the body of results and can either be {} (singleton) or [] array. My API will always use an array. I created  a class MQLMultipleResultResponse to hold the response envelop. For now I’m ignoring the other items such as status.

public class MQLMultipleResultResponse {
	private T[] result;

	public T[] getResult() {
		return result;
	}
	public void setResult(T[] result) {
		this.result = result;
	}
}

The query can be executed with or without a type. If the type is absent the underlying code will use Object. The type defines what kind of of object array will be returned. For example, if Person.class is specified an array of Person will be returned.

The underlying code executes the following …

URI uri = new URI(entityManager.getScheme(), entityManager.getAuthority(), entityManager.getPath(), Util.createQuery(variables), null);

ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
ClientHttpRequest request = requestFactory.createRequest(uri, HttpMethod.GET);
ClientHttpResponse response = request.execute();
ObjectMapper objectMapper = new ObjectMapper();
MQLMultipleResultResponse body = objectMapper.readValue(response.getBody(), typeReference);
results = body.getResult();

Most of this is straight forward HTTP client and JSON calls. The use of objectMapper.readValue() is somewhat unique. Since MQLMultipleResultResponse uses a generic I initially ran into problems with the following …

objectMapper.readValue(response.getBody(), MQLMultipleResultResponse.class);

objectMapper.readValue() cannot know what type the result body will be so it defaults to a Map. The following isn’t legal …

objectMapper.readValue(response.getBody(), MQLMultipleResultResponse.class);

Fortunately Jackson provides an alternate. Instead of passing the Class you can pass a TypeReference. The following code creates the necessary TypeReference …

TypeReference<MQLMultipleResultResponse> typeReference =
  new TypeReference<MQLMultipleResultResponse>() {
    public Type getType() {
      return ParameterizedTypeImpl.make(MQLMultipleResultResponse.class, new Type[] { resultClass }, null    );
  }
};

The key is providing the correct Type. In this case the subclass ParameterizedType. ParameterizedTypeImpl implements ParameterizedType. Java contains a version of ParameterizedTypeImpl but it’s inaccessible. Implementing ParameterizedType is just complicated enough that I decided to just copy ParameterizedTypeImpl’s source code into my project. It comes with a helpful make() method.

With all this in place the test client generates the following results. In one case an array of objects and in the other an array of Person.

result: {name=Dan Milbrath, type=/people/person, id=/en/dan_milbrath}
result: {name=David Safavian, type=/people/person, id=/en/david_safavian}
result: {name=Robert Cook, type=/people/person, id=/en/robert_cook}

result: org.bwgz.freebase.model.Person@2a444: getName: Dan Milbrath; getId: /en/dan_milbrath; getType: /people/person; getClass: class org.bwgz.freebase.model.Person; }
result: org.bwgz.freebase.model.Person@c126b3: getName: David Safavian; getId: /en/david_safavian; getType: /people/person; getClass: class org.bwgz.freebase.model.Person; }
result: org.bwgz.freebase.model.Person@1c01b97: getName: Robert Cook; getId: /en/robert_cook; getType: /people/person; getClass: class org.bwgz.freebase.model.Person; }
Advertisements
Categories: Freebase, Java, JPA, Structured Data
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: