
Objective
This article will show you how to implement a simple search in Google App Engine using JDO engine including searching in child objects.
The Problem Domain
In my application I need to search for data and you probably need to do the same in yours. In Google App Engine you can not query for properties of the child objects. In SQL world this means you can not use “where” clause. So how we can not construct a query which looks at child objects. So how can we implement search in such restrictive environment?
The Solution
I came up with a very simple solution – I created an “index” property where I store all the data that I need to search on. Let’s say we have a Customer bean that has multiple addresses and phones. We want to be able to search on address, phone and customer name. Here are our beans:
@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable="true") public class Customer implements Serializable { private static final long serialVersionUID = -3665292067832675548L; @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Long id; @Persistent private String name; @Persistent private String contactName; @Persistent private String comments; @Persistent(mappedBy = "customer") @Element(dependent = "true") private List<Address> addresses = new ArrayList<Address>(); @Persistent(mappedBy = "customer") @Element(dependent = "true") private List<Phone> phones = new ArrayList<Phone>(); @Persistent private List<String> index; // Getters and Setter go here... } @PersistenceCapable(identityType = IdentityType.APPLICATION, detachable="true") public class Address { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Key id; @Persistent private String type; @Persistent private String name; @Persistent private String line1; @Persistent private String line2; @Persistent private String city; @Persistent private String state; @Persistent private String zip; @Persistent private Customer customer; // Getters and Setter go here... } @PersistenceCapable(identityType = IdentityType.APPLICATION, detachable="true") public class Phone { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Key id; @Persistent private String type; @Persistent private Number phone; @Persistent private Customer customer; // Getters and Setter go here... }
In the Customer bean we defined an “index” property which is a String array. We will put all our searchable words int this index, so later we can construct a query. Before we save our customer we want to populate index property like this:
public void rebuildIndex() { index = new ArrayList<String>(); Iterator<Address> it = this.addresses.iterator(); while( it.hasNext() ) { Address ad = it.next(); if( ad.getLine1() != null ){ String[] idx = ad.getLine1().split(" "); for( int i = 0 ; i < idx.length ; i++ ) index.add( idx[i] ); } if( ad.getLine2() != null ){ String[] idx = ad.getLine2().split(" "); for( int i = 0 ; i < idx.length ; i++ ) index.add( idx[i] ); } } Iterator<Phone> it2 = this.phones.iterator(); while( it2.hasNext() ) { Phone ph = it2.next(); if( ph.getPhone() != null ) index.add( ph.getPhone().toString() ); } String[] idx = name.split(" "); for( int i = 0 ; i < idx.length ; i++ ) index.add( idx[i] ); idx = contactName.split(" "); for( int i = 0 ; i < idx.length ; i++ ) index.add( idx[i] ); }
Now we have all our searchable words from child beans and parent bean in one property we can search on. Here is how my search method looks like:
@SuppressWarnings("unchecked") @ModelAttribute("custs") @RequestMapping(value = "/{search}", method = RequestMethod.GET) public List<Customer> searchCustomers( @PathVariable String search, HttpSession session) throws IOException { PersistenceManager pm = pmf.getManager(); Query query = pm.newQuery(Customer.class); query.setFilter("index == searchParam"); query.declareParameters("String searchParam"); List<Customer> custs; try { custs = (List<Customer>) query.execute( search ); } finally { query.closeAll(); } return custs; }
Conclusion
We found an easy way around the big table JDO implementation limitation in Google App Engine. It is a simple and strait forward workaround, but it has couple drawbacks: we can only search on full words and we duplicate data using more storage space. In my book storage is cheap so that is not a big concern for me.
As I progress more with my projects on Google App Engine, I will surely revisit the search topic and post about other solutions to this challenge.
If you have a better solution please post it in the comments, I would love to give it a try!


Hi Tomas,
Thanks for the great posts. I’ve read them all today and have found them extremely helpful.
I just wanted to ask about the Key property. You seem to use it for some primary keys and you use Long for some others. Is there a reason for this?
thanks
The reason you have to use Key is mandated for the child objects. Key is actually combination of 2 ids: parent and child. You can also use it for parent objects, but I like Long for simplicity. If you try using Long for child key Data Nucleus will complain very loudly
Tomas