au.edu.anu.metadatastores.rdf.RDFService.java Source code

Java tutorial

Introduction

Here is the source code for au.edu.anu.metadatastores.rdf.RDFService.java

Source

/*******************************************************************************
 * Australian National University Metadata Stores
 * Copyright (C) 2013  The Australian National University
 * 
 * This file is part of Australian National University Metadata Stores.
 * 
 * Australian National University Metadata Stores is free software: you
 * can redistribute it and/or modify it under the terms of the GNU
 * General Public License as published by the Free Software Foundation,
 * either version 3 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package au.edu.anu.metadatastores.rdf;

import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;

import org.hibernate.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import au.edu.anu.metadatastores.datamodel.store.Item;
import au.edu.anu.metadatastores.datamodel.store.ItemRelation;
import au.edu.anu.metadatastores.datamodel.store.ItemRelationId;
import au.edu.anu.metadatastores.datamodel.store.SystemType;
import au.edu.anu.metadatastores.datamodel.store.annotations.ItemAttributeTrait;
import au.edu.anu.metadatastores.datamodel.store.annotations.TraitType;
import au.edu.anu.metadatastores.rdf.annotation.RDFDefaultTriple;
import au.edu.anu.metadatastores.rdf.annotation.RDFSet;
import au.edu.anu.metadatastores.rdf.annotation.RDFSetWithDefault;
import au.edu.anu.metadatastores.rdf.annotation.RDFSets;
import au.edu.anu.metadatastores.rdf.annotation.RDFType;
import au.edu.anu.metadatastores.rdf.annotation.RDFUri;
import au.edu.anu.metadatastores.rdf.exception.RDFException;
import au.edu.anu.metadatastores.rdf.namespace.RDFNS;
import au.edu.anu.metadatastores.rdf.namespace.StoreNS;
import au.edu.anu.metadatastores.rdf.namespace.VCardNS;
import au.edu.anu.metadatastores.rdf.parser.RDFAnnotationParser;
import au.edu.anu.metadatastores.services.store.StoreHibernateUtil;
import au.edu.anu.metadatastores.store.misc.Subject;
import au.edu.anu.metadatastores.store.search.ItemDTO;
import au.edu.anu.metadatastores.store.search.Search;
import au.edu.anu.metadatastores.store.search.SearchTerm;
import au.edu.anu.metadatastores.store.util.ItemResolver;
import au.edu.anu.metadatastores.util.properties.PropertyLoader;

import com.hp.hpl.jena.graph.NodeFactory;
import com.hp.hpl.jena.graph.Triple;
import com.hp.hpl.jena.query.Query;
import com.hp.hpl.jena.query.QueryExecution;
import com.hp.hpl.jena.query.QueryExecutionFactory;
import com.hp.hpl.jena.query.QueryFactory;
import com.hp.hpl.jena.query.QuerySolution;
import com.hp.hpl.jena.query.ResultSet;
import com.hp.hpl.jena.rdf.model.Literal;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import com.hp.hpl.jena.sparql.core.Var;
import com.hp.hpl.jena.sparql.expr.E_IsBlank;
import com.hp.hpl.jena.sparql.expr.E_IsIRI;
import com.hp.hpl.jena.sparql.expr.E_OneOf;
import com.hp.hpl.jena.sparql.expr.E_Regex;
import com.hp.hpl.jena.sparql.expr.Expr;
import com.hp.hpl.jena.sparql.expr.ExprList;
import com.hp.hpl.jena.sparql.expr.ExprVar;
import com.hp.hpl.jena.sparql.modify.UpdateProcessRemote;
import com.hp.hpl.jena.sparql.modify.request.QuadAcc;
import com.hp.hpl.jena.sparql.modify.request.UpdateDeleteInsert;
import com.hp.hpl.jena.sparql.syntax.Element;
import com.hp.hpl.jena.sparql.syntax.ElementFilter;
import com.hp.hpl.jena.sparql.syntax.ElementGroup;
import com.hp.hpl.jena.sparql.syntax.ElementOptional;
import com.hp.hpl.jena.sparql.syntax.ElementTriplesBlock;
import com.hp.hpl.jena.sparql.syntax.ElementUnion;
import com.hp.hpl.jena.sparql.util.ExprUtils;
import com.hp.hpl.jena.update.UpdateExecutionFactory;
import com.hp.hpl.jena.update.UpdateFactory;
import com.hp.hpl.jena.update.UpdateRequest;

/**
 * <p>RDFService</p>
 *
 * <p>The Australian National University</p>
 *
 * <p>Service class for performing actions on an RDF base. i.e. it updates and searches the RDF Triple store</p>
 *
 * @author Genevieve Turner
 *
 */
public class RDFService implements Search {
    //TODO check if comparing values to insert/delete would be faster than the update
    static final Logger LOGGER = LoggerFactory.getLogger(RDFService.class);

    private static RDFService singleton_;
    private Properties properties_;

    /**
     * Constructor
     */
    private RDFService() {
        properties_ = PropertyLoader.loadProperties("rdf.properties");

        if (properties_ == null) {
            LOGGER.debug("RDFService.init(..): No rdf service property file found.");
        }
    }

    /**
     * Get the RDF Service
     * 
     * @return The RDF Service object
     */
    public static RDFService getSingleton() {
        if (singleton_ == null) {
            singleton_ = new RDFService();
        }

        return singleton_;
    }

    /**
     * Get all the objects from the metadata stores database and reference them.
     */
    public void updateAll() {
        LOGGER.debug("In Update All");
        Session session = StoreHibernateUtil.getSessionFactory().openSession();
        try {
            //TODO remove the limit however for testing purposes the limit creates issues
            org.hibernate.Query query = session.createQuery("FROM Item");
            query.setMaxResults(10);
            @SuppressWarnings("unchecked")
            List<Item> items = query.list();
            update(items);
        } finally {
            session.close();
        }
    }
    //TODO add data commons and dublin core records to the mix

    //TODO test this
    public void updateModifiedAfterDate(Date modifiedDate) {
        Session session = StoreHibernateUtil.getSessionFactory().openSession();
        try {
            org.hibernate.Query query = session.createQuery(
                    "SELECT item FROM Item item WHERE EXISTS ( SELECT 1 FROM item.itemAttributes ia WHERE ia.lastModified > :lastModifiedDate ) ");
            query.setParameter("lastModifiedDate", modifiedDate);

            @SuppressWarnings("unchecked")
            List<Item> items = query.list();
            update(items);
        } finally {
            session.close();
        }
    }

    public void updateFromSpecifiedSystem(String extSystem) {
        Session session = StoreHibernateUtil.getSessionFactory().openSession();
        try {
            org.hibernate.Query query = session.createQuery("FROM Item WHERE extSystem = :extSystem");
            query.setParameter("extSystem", extSystem);

            @SuppressWarnings("unchecked")
            List<Item> items = query.list();
            update(items);
        } finally {
            session.close();
        }
    }

    /**
     * Update the system types
     */
    public void updateSystemTypes() {
        Session session = StoreHibernateUtil.getSessionFactory().openSession();
        try {
            org.hibernate.Query query = session.createQuery("FROM SystemType");
            //TODO add graph capabilities into code!
            @SuppressWarnings("unchecked")
            List<SystemType> systemTypes = query.list();
            for (SystemType sysType : systemTypes) {
                save(sysType.getExtSystem(), sysType);
            }
        } finally {
            session.close();
        }
    }

    /**
     * Update the record with the given id
     * 
     * @param id The id
     */
    public void update(Long id) {
        Session session = StoreHibernateUtil.getSessionFactory().openSession();

        try {
            Object item = session.get(Item.class, id);
            update(Arrays.asList((Item) item));
        } finally {
            session.close();
        }
    }

    /**
     * Update the triplestore with the given items
     * 
     * @param items The list of items to update the triplestore for
     */
    private void update(List<Item> items) {
        RDFAnnotationParser parser = new RDFAnnotationParser();
        int numCount = 0;
        int numAtOnce = 50;
        Model model = ModelFactory.createDefaultModel();
        List<Triple> deleteTriples = new ArrayList<Triple>();
        List<String> objectURIs = new ArrayList<String>();
        for (Item item : items) {
            if (!Boolean.FALSE.equals(item.getDeleted())) {
                Object obj = ItemResolver.resolve(item);
                try {
                    Resource resource = parser.getResource(item.getIid().toString(), item.getExtSystem(), obj,
                            model);
                    //Resource resource = parser.getResource(item.getIid().toString(), obj, model);
                    addRelationsToModel(model, item.getItemRelationsForIid());
                    LOGGER.info("URI: {}", resource.getURI());
                    objectURIs.add(resource.getURI());
                    numCount++;
                } catch (RDFException e) {
                    LOGGER.error("Exception retrieving resource");
                }
                if (numCount % numAtOnce == 0) {
                    saveUpdates(model, objectURIs);

                    deleteTriples.clear();
                    objectURIs.clear();
                    model = ModelFactory.createDefaultModel();
                    StmtIterator it = model.listStatements();
                    if (it.hasNext()) {
                        LOGGER.info("Still Values in Iterator?");
                    }
                }
            }
        }

        saveUpdates(model, objectURIs);
    }

    private void addRelationsToModel(Model model, Set<ItemRelation> relations) {
        for (ItemRelation relation : relations) {
            ItemRelationId id = relation.getId();
            Resource iid = model.createResource(properties_.getProperty("rdf.object.uri") + id.getIid().toString());
            Resource relatedIid = model
                    .createResource(properties_.getProperty("rdf.object.uri") + id.getRelatedIid().toString());
            iid.addProperty(
                    model.createProperty(properties_.getProperty("rdf.relation.uri") + id.getRelationValue()),
                    relatedIid);
        }
    }

    private void saveUpdates(Model insertModel, List<String> objectURIs) {
        LOGGER.info("Number of objects to update: {}", objectURIs.size());
        if (objectURIs.size() == 0) {
            return;
        }

        ElementUnion union = new ElementUnion();
        union.addElement(getChildToDeleteElement(objectURIs));
        union.addElement(getDirectToDeleteElement(objectURIs));

        UpdateDeleteInsert delUpdate = new UpdateDeleteInsert();
        QuadAcc delQuad = delUpdate.getDeleteAcc();
        delQuad.addTriple(new Triple(Var.alloc("o"), Var.alloc("q"), Var.alloc("r")));

        delUpdate.setElement(union);

        if (LOGGER.isDebugEnabled()) {
            LOGGER.info("Update Query: {}", delUpdate.toString());
        }
        String endPoint = properties_.getProperty("rdf.server") + properties_.getProperty("rdf.server.update");
        UpdateProcessRemote riStore = (UpdateProcessRemote) UpdateExecutionFactory.createRemote(delUpdate,
                endPoint);
        riStore.execute();

        UpdateDeleteInsert update = new UpdateDeleteInsert();
        QuadAcc insertQuads = update.getInsertAcc();

        StmtIterator it = insertModel.listStatements();
        while (it.hasNext()) {
            Statement stmt = it.next();
            stmt.asTriple();
            insertQuads.addTriple(stmt.asTriple());
        }

        if (LOGGER.isDebugEnabled()) {
            LOGGER.info("Update Query: {}", update.toString());
        }

        UpdateProcessRemote riStore2 = (UpdateProcessRemote) UpdateExecutionFactory.createRemote(update, endPoint);
        riStore2.execute();
    }

    /**
     * Update the given item
     * 
     * @param item The item to update
     */
    public void update(Item item) {
        LOGGER.debug("Updating record: {}", item.getIid());
        Object obj = ItemResolver.resolve(item);
        save(item.getIid().toString(), obj);
        saveRelations(item);
    }

    /**
     * Save the object
     * 
     * @param id The id of the object
     * @param object The object to save
     */
    private void save(String id, Object object) {
        RDFAnnotationParser parser = new RDFAnnotationParser();
        save(id, object, parser);
    }

    /**
     * Save the object 
     * 
     * @param id THe id of the object
     * @param object The object to save
     * @param parser The parser to use
     */
    private void save(String id, Object object, RDFAnnotationParser parser) {
        Model model = ModelFactory.createDefaultModel();
        try {
            try {
                Resource resource = parser.getResource(id, object, model);
                if (resource == null) {
                    LOGGER.error("Resource is null for some reason!");
                    return;
                }
                List<String> objectURIs = new ArrayList<String>();
                objectURIs.add(resource.getURI());
                saveUpdates(model, objectURIs);
            } catch (RDFException e) {
                LOGGER.error("Exception saving resource", e);
            } catch (Exception e) {
                LOGGER.error("Exception saving resource", e);
            }
        } finally {
            model.close();
        }
    }

    /**
     * Save the relations of the item
     * 
     * @param item The item to extract and save the relations of
     */
    private void saveRelations(Item item) {
        saveRelations(item.getItemRelationsForIid());
        saveRelations(item.getItemRelationsForRelatedIid());

    }

    /**
     * Save the relations from the given set
     * 
     * @param relations The set of relations to save
     */
    private void saveRelations(Set<ItemRelation> relations) {
        Model model = ModelFactory.createDefaultModel();
        for (ItemRelation relation : relations) {
            ItemRelationId id = relation.getId();
            Resource iid = model.createResource(properties_.getProperty("rdf.object.uri") + id.getIid().toString());
            Resource relatedIid = model
                    .createResource(properties_.getProperty("rdf.object.uri") + id.getRelatedIid().toString());
            iid.addProperty(
                    model.createProperty(properties_.getProperty("rdf.relation.uri") + id.getRelationValue()),
                    relatedIid);
        }
        updateRemote(model);
    }

    /*private Element getChildToDeleteElement(List<String> uris) {
       // Create the part of the where query that finds blank nodes associated with the given record
       ElementUnion union = new ElementUnion();
       for (String uri : uris) {
     Triple uriTriple = Triple.create(NodeFactory.createURI(uri), Var.alloc("p"), Var.alloc("o"));
     Triple fieldTriple = Triple.create(Var.alloc("o"), Var.alloc("q"), Var.alloc("r"));
         
     ElementTriplesBlock relatedElementTriples = new ElementTriplesBlock();
     relatedElementTriples.addTriple(uriTriple);
     relatedElementTriples.addTriple(fieldTriple);
        
     Expr oVarIsBlank = new E_IsBlank(new ExprVar("o"));
     ElementFilter oIsBlankFilter = new ElementFilter(oVarIsBlank);
        
     ElementGroup blankRelGroup = new ElementGroup();
     blankRelGroup.addElement(relatedElementTriples);
     blankRelGroup.addElementFilter(oIsBlankFilter);
         
     union.addElement(blankRelGroup);
       }
           
       return union;
    }*/

    private Element getChildToDeleteElement(List<String> uris) {
        // Create the part of the where query that finds blank nodes associated with the given record
        Triple uriTriple = Triple.create(Var.alloc("s"), Var.alloc("p"), Var.alloc("o"));
        Triple fieldTriple = Triple.create(Var.alloc("o"), Var.alloc("q"), Var.alloc("r"));

        ElementTriplesBlock relatedElementTriples = new ElementTriplesBlock();
        relatedElementTriples.addTriple(uriTriple);
        relatedElementTriples.addTriple(fieldTriple);

        Expr oVarIsBlank = new E_IsBlank(new ExprVar("o"));
        ElementFilter oIsBlankFilter = new ElementFilter(oVarIsBlank);

        ExprList uriExpr = new ExprList();
        for (String uri : uris) {
            uriExpr.add(ExprUtils.nodeToExpr(NodeFactory.createURI(uri)));
        }
        Expr sOneOf = new E_OneOf(new ExprVar("s"), uriExpr);
        ElementFilter sVarOneOfFilter = new ElementFilter(sOneOf);

        ElementGroup blankRelGroup = new ElementGroup();
        blankRelGroup.addElement(relatedElementTriples);
        blankRelGroup.addElementFilter(oIsBlankFilter);
        blankRelGroup.addElementFilter(sVarOneOfFilter);
        return blankRelGroup;
    }

    /*private Element getDirectToDeleteElement(List<String> uris) {
       ElementUnion union = new ElementUnion();
       for (String uri : uris) {
     ElementTriplesBlock block = new ElementTriplesBlock();
     Triple fieldTriple = Triple.create(NodeFactory.createURI(uri), Var.alloc("q"), Var.alloc("r"));
     block.addTriple(fieldTriple);
     union.addElement(block);
       }
       return union;
    }*/

    private Element getDirectToDeleteElement(List<String> uris) {
        Triple fieldTriple = Triple.create(Var.alloc("o"), Var.alloc("q"), Var.alloc("r"));

        // Create the part of the where query that finds triples associated with the record
        ElementTriplesBlock triplesWithURI = new ElementTriplesBlock();
        triplesWithURI.addTriple(fieldTriple);

        ExprList uriExpr = new ExprList();
        for (String uri : uris) {
            uriExpr.add(ExprUtils.nodeToExpr(NodeFactory.createURI(uri)));
        }
        Expr oOneOf = new E_OneOf(new ExprVar("o"), uriExpr);
        ElementFilter oVarOneOfFilter = new ElementFilter(oOneOf);

        ElementGroup elementWithURI = new ElementGroup();
        elementWithURI.addElement(triplesWithURI);
        elementWithURI.addElement(oVarOneOfFilter);
        return elementWithURI;
    }

    /**
     * Execute the update
     * 
     * @param model The model to save
     */
    public void updateRemote(Model model) {
        StringWriter sw = new StringWriter();
        model.write(sw, "N-TRIPLES");
        updateRemote(sw.toString());
    }

    /**
     * Execute the update
     * 
     * @param nTripleString The string of triples to save
     */
    private void updateRemote(String nTripleString) {
        if (nTripleString.length() > 0) {
            // It would be nice to have something to generate and insert the data rather than needing to write it to a n-triple string then adding 'INSERT DATA around the string
            UpdateRequest update = UpdateFactory.create("INSERT DATA { " + nTripleString + " }");
            LOGGER.debug("Insert Query: {}", update.toString());
            UpdateProcessRemote riStore = (UpdateProcessRemote) UpdateExecutionFactory.createRemote(update,
                    properties_.getProperty("rdf.server") + properties_.getProperty("rdf.server.update"));
            riStore.execute();
        }
    }

    @Override
    public List<ItemDTO> search(String value) {
        String numStr = "1";
        Element childFieldsElement = getChildFieldsElement(numStr, value);
        Element directFieldsElement = getDirectFieldsElement(numStr, value);
        Element associatedPeople = getAssociatedPeopleElement(numStr, value);

        ElementUnion union = new ElementUnion();
        union.addElement(directFieldsElement);
        union.addElement(childFieldsElement);
        union.addElement(associatedPeople);

        //Expr expr3 = new E_Regex(new ExprVar(numStr), value, "i");
        //ElementFilter ef3 = new ElementFilter(expr3);

        Element titleOpt = getOptionalTitleElement();
        Element fullNameOpt = getOptionalFullNameElementAsTitle();
        Element extSystemOpt = getExtSystemElement();

        //Construct the element query
        ElementGroup elG3 = new ElementGroup();
        elG3.addElement(union);
        //elG3.addElementFilter(ef3);
        elG3.addElement(titleOpt);
        elG3.addElement(fullNameOpt);
        elG3.addElement(extSystemOpt);

        Query query = QueryFactory.make();
        query.setQueryPattern(elG3);
        query.setQuerySelectType();
        query.addResultVar("id");
        query.addResultVar("title");
        query.addResultVar("extsystem");

        return executeItemDTOQuery(query);
    }

    /**
     * Execute the given query on the RDF triplestore
     * 
     * @param query The query to execute
     * @return The list of items returned
     */
    private List<ItemDTO> executeItemDTOQuery(Query query) {
        String endPointAddress = properties_.getProperty("rdf.server")
                + properties_.getProperty("rdf.server.query");
        LOGGER.debug("Query: {}", query.toString());
        QueryExecution qExec = QueryExecutionFactory.sparqlService(endPointAddress, query);

        List<ItemDTO> items = new ArrayList<ItemDTO>();
        ItemDTO item = null;
        try {
            ResultSet results = qExec.execSelect();
            while (results.hasNext()) {
                item = new ItemDTO();
                QuerySolution soln = results.nextSolution();
                Resource id = soln.getResource("id");
                item.setId(parseId(id.getURI()));
                Literal title = soln.getLiteral("title");
                if (title != null) {
                    item.setTitle(title.toString());
                }
                Literal extSystem = soln.getLiteral("extsystem");
                if (extSystem != null) {
                    item.setExtSystem(extSystem.toString());
                }
                items.add(item);
            }
        } finally {
            qExec.close();
        }
        return items;
    }

    /**
     * Generate the Triples Element that will retrieve child fields
     * 
     * @param numStr The id for this set of triples
     * @return The generated Element
     */
    private Element getChildFieldsElement(String numStr, String value) {
        Triple triple1 = new Triple(Var.alloc("id"), Var.alloc(numStr + "_2"), Var.alloc(numStr + "_3"));
        Triple triple2 = new Triple(Var.alloc(numStr + "_3"), Var.alloc(numStr + "_1"), Var.alloc(numStr));
        Expr expr = new E_IsBlank(new ExprVar(numStr + "_3"));

        ElementTriplesBlock relatedTriples = new ElementTriplesBlock();
        relatedTriples.addTriple(triple1);
        relatedTriples.addTriple(triple2);
        ElementFilter filter = new ElementFilter(expr);

        Expr valueExpr = new E_Regex(new ExprVar(numStr), value, "i");
        ElementFilter valueFilter = new ElementFilter(valueExpr);

        ElementGroup group = new ElementGroup();
        group.addElement(relatedTriples);
        group.addElementFilter(filter);
        group.addElementFilter(valueFilter);
        return group;
    }

    /**
     * Generate the Triples Element that will retrieve fields that are directly associated with the id
     * 
     * @param numStr The id for this set of triples
     * @return The generated Element
     */
    private Element getDirectFieldsElement(String numStr, String value) {
        Triple triple = new Triple(Var.alloc("id"), Var.alloc(numStr + "_1"), Var.alloc(numStr));
        Expr expr2 = new E_IsIRI(new ExprVar("id"));
        ElementFilter filter = new ElementFilter(expr2);

        Expr valueExpr = new E_Regex(new ExprVar(numStr), value, "i");
        ElementFilter valueFilter = new ElementFilter(valueExpr);
        ElementGroup group = new ElementGroup();
        group.addTriplePattern(triple);
        group.addElementFilter(filter);
        group.addElementFilter(valueFilter);
        return group;
    }

    /**
     * Generate the Triples Element that will retrieve records for which the name of a person has been found
     * 
     * @param numStr The id for this set of triples
     * @return The generated element
     */
    private Element getAssociatedPeopleElement(String numStr, String value) {
        Triple t1 = new Triple(Var.alloc("id"), Var.alloc(numStr + "_5"), Var.alloc(numStr + "_4"));
        Triple t2 = new Triple(Var.alloc(numStr + "_4"), NodeFactory.createURI(RDFNS.TYPE),
                NodeFactory.createURI(properties_.getProperty("rdf.object.uri") + "PERSON"));
        Triple t3 = new Triple(Var.alloc(numStr + "_4"), Var.alloc(numStr + "_6"), Var.alloc(numStr));

        Expr valueExpr = new E_Regex(new ExprVar(numStr), value, "i");
        ElementFilter valueFilter = new ElementFilter(valueExpr);

        ElementTriplesBlock triplesBlock = new ElementTriplesBlock();
        triplesBlock.addTriple(t1);
        triplesBlock.addTriple(t2);
        triplesBlock.addTriple(t3);

        ElementGroup group = new ElementGroup();
        group.addElement(triplesBlock);
        group.addElementFilter(valueFilter);

        return group;
    }

    /**
     * Get the optional title element
     * 
     * @return The generated element
     */
    private Element getOptionalTitleElement() {
        Triple titleTriple = new Triple(Var.alloc("id"), NodeFactory.createURI(StoreNS.TITLE), Var.alloc("title"));
        ElementTriplesBlock titleElement = new ElementTriplesBlock();
        titleElement.addTriple(titleTriple);
        ElementOptional titleOpt = new ElementOptional(titleElement);
        return titleOpt;
    }

    /**
     * Get a persons name as the title element
     * 
     * @return The generated element
     */
    private Element getOptionalFullNameElementAsTitle() {
        //Create the optional full name element as a title
        Triple fullNameTriple = new Triple(Var.alloc("id"), NodeFactory.createURI(VCardNS.FN), Var.alloc("title"));
        ElementTriplesBlock fullNameElement = new ElementTriplesBlock();
        fullNameElement.addTriple(fullNameTriple);
        ElementOptional fullNameOpt = new ElementOptional(fullNameElement);
        return fullNameOpt;
    }

    /**
     * Get the system type element
     * 
     * @return The generated element
     */
    private Element getExtSystemElement() {
        Triple extSystemTriple = new Triple(Var.alloc("id"), NodeFactory.createURI(RDFNS.TYPE), Var.alloc("t"));
        Triple t2 = new Triple(Var.alloc("t"), NodeFactory.createURI(StoreNS.TITLE), Var.alloc("extsystem"));
        ElementTriplesBlock extSystemElement = new ElementTriplesBlock();
        extSystemElement.addTriple(extSystemTriple);
        extSystemElement.addTriple(t2);
        return extSystemElement;
    }

    /**
     * Get the Long id value instead of the uri
     * 
     * @param uri The uri to find the Long value of
     * @return The Long id
     */
    private Long parseId(String uri) {
        String idStr = uri.substring(uri.lastIndexOf("/") + 1);
        LOGGER.debug("idStr: {}", idStr);
        Long id = new Long(idStr);
        return id;
    }

    @Override
    public List<ItemDTO> search(String system, List<SearchTerm> searchTerms) {

        ElementGroup queryPattern = null;
        if (system == null || system.trim().length() == 0) {
            queryPattern = searchWithoutSystem(searchTerms);
        } else {
            queryPattern = searchWithSystem(system, searchTerms);
        }

        queryPattern.addElement(getOptionalTitleElement());
        queryPattern.addElement(getOptionalFullNameElementAsTitle());
        queryPattern.addElement(getExtSystemElement());

        Query query = QueryFactory.make();
        query.setQueryPattern(queryPattern);
        query.setQuerySelectType();
        query.addResultVar("id");
        query.addResultVar("title");
        query.addResultVar("extsystem");

        return executeItemDTOQuery(query);
    }

    /**
     * Search all the records for the given values with no system type defined
     * 
     * @param searchTerms The search terms
     * @return The element group representing a search without a system type
     */
    private ElementGroup searchWithoutSystem(List<SearchTerm> searchTerms) {
        int i = 1;

        // Due to the difficulties posed we are essentially ignoring the search term field type
        ElementGroup elG1 = new ElementGroup();
        for (SearchTerm searchTerm : searchTerms) {
            String numStr = Integer.valueOf(i++).toString();
            elG1.addElement(getAllElement(searchTerm, null, numStr));
        }

        return elG1;
    }

    /**
     * Search and filter with the given system type
     * 
     * @param system The type of objects to return
     * @param searchTerms The terms to  search for
     * @return The element group representing the search
     */
    private ElementGroup searchWithSystem(String system, List<SearchTerm> searchTerms) {
        LOGGER.debug("Execute Query With Field Limits");
        Class<?> clazz = ItemResolver.resolveTypeBySystemId(system);

        ElementTriplesBlock block = new ElementTriplesBlock();
        if (clazz.isAnnotationPresent(RDFType.class)) {
            RDFType rdfType = clazz.getAnnotation(RDFType.class);
            Triple t1 = new Triple(Var.alloc("id"), NodeFactory.createURI(RDFNS.TYPE),
                    NodeFactory.createURI(properties_.getProperty("rdf.object.uri") + rdfType.value()));
            block.addTriple(t1);
        }
        Map<String, String> searchValues = new HashMap<String, String>();
        Method[] methods = clazz.getMethods();
        int i = 1;
        RDFSets sets = null;
        if (clazz.isAnnotationPresent(RDFSets.class)) {
            sets = clazz.getAnnotation(RDFSets.class);
        }
        ElementGroup elG1 = new ElementGroup();
        for (SearchTerm searchTerm : searchTerms) {
            if (searchTerm.getField() == null || searchTerm.getField().length() == 0) {
                String numStr = Integer.valueOf(i++).toString();
                elG1.addElement(getAllElement(searchTerm, searchValues, numStr));
            }
        }
        List<Triple> tripleList = new ArrayList<Triple>();
        for (Method method : methods) {
            if (method.isAnnotationPresent(ItemAttributeTrait.class)) {
                ItemAttributeTrait trait = method.getAnnotation(ItemAttributeTrait.class);
                if (trait.traitType().equals(TraitType.SUBJECT_LIST)) {
                    if (method.isAnnotationPresent(RDFUri.class)) {
                        for (SearchTerm searchTerm : searchTerms) {
                            Method[] subjMethods = Subject.class.getMethods();
                            for (Method subjMethod : subjMethods) {
                                if (subjMethod.isAnnotationPresent(ItemAttributeTrait.class)) {
                                    ItemAttributeTrait subjTrait = subjMethod
                                            .getAnnotation(ItemAttributeTrait.class);
                                    if (subjTrait.attrType().equals(searchTerm.getField())) {
                                        if (subjMethod.isAnnotationPresent(RDFUri.class)) {
                                            String numStr = Integer.valueOf(i++).toString();
                                            tripleList.addAll(getSubjectTriples(method, subjMethod, searchTerm,
                                                    searchValues, numStr));
                                        }
                                    }
                                }
                            }
                        }
                    }
                } else {
                    String traitVal = trait.attrType();
                    for (SearchTerm searchTerm : searchTerms) {
                        if (traitVal.equals(searchTerm.getField())) {
                            String numStr = Integer.valueOf(i++).toString();
                            tripleList.addAll(getSearchTermElement(sets, method, searchTerm, searchValues, numStr));
                        }
                    }
                }
            }
        }
        for (Triple triple : tripleList) {
            block.addTriple(triple);
        }
        elG1.addElement(block);
        for (Entry<String, String> entry : searchValues.entrySet()) {
            elG1.addElementFilter(getCaseInsensitiveRegexFilter(entry.getKey(), entry.getValue()));
        }

        return elG1;
    }

    /**
     * Generate a filter object with the given field id and value
     * 
     * @param fieldId The field id
     * @param value The value to filter for
     * @return The filter
     */
    private ElementFilter getCaseInsensitiveRegexFilter(String fieldId, String value) {
        Expr regex = new E_Regex(new ExprVar(fieldId), value, "i");
        ElementFilter filter = new ElementFilter(regex);

        return filter;
    }

    /**
     * Generate the search term triples
     * 
     * @param sets The RDFSets associated with the system types class
     * @param method The method to get the search terms for
     * @param searchTerm The search term to generate triples for
     * @param searchValues The values to generate filters with
     * @param numStr The id number to associate with triples
     * @return A list of generated triples
     */
    private List<Triple> getSearchTermElement(RDFSets sets, Method method, SearchTerm searchTerm,
            Map<String, String> searchValues, String numStr) {
        if (method.isAnnotationPresent(RDFUri.class)) {
            RDFUri rdfUri = method.getAnnotation(RDFUri.class);
            boolean found = false;
            if (sets != null) {
                String methodField = method.getName();
                methodField = methodField.substring(3).toLowerCase();
                RDFSet[] rdfSets = sets.sets();
                for (int i = 0; !found && i < rdfSets.length; i++) {
                    RDFSet set = rdfSets[i];
                    String[] fields = set.fields();
                    for (int j = 0; j < fields.length; j++) {
                        String field = fields[j];

                        if (methodField.equals(field.toLowerCase())) {
                            found = true;

                            return getSetTriples(rdfUri, searchTerm, searchValues, numStr, set);
                        }
                    }
                }
                RDFSetWithDefault[] rdfSetsDef = sets.setsWithDefaults();
                for (int i = 0; !found && i < rdfSetsDef.length; i++) {
                    RDFSetWithDefault set = rdfSetsDef[i];
                    if (methodField.equals(set.field().toLowerCase())) {
                        found = true;

                        return getDefaultSetTriples(rdfUri, searchTerm, searchValues, numStr, set);
                    }
                }
                if (!found) {
                    return getSingleTriple(rdfUri, searchTerm, searchValues, numStr);
                }
            } else {
                return getSingleTriple(rdfUri, searchTerm, searchValues, numStr);
            }
        }
        LOGGER.info("Returning null for method: {}", method.getName());
        return null;
    }

    /**
     * Generated a triple from the given information
     * 
     * @param rdfUri The RDFUri object associated with the method for the search term
     * @param searchTerm The search term
     * @param searchValues The values to generate filters with
     * @param numStr The id number to associate with triples
     * @return
     */
    private List<Triple> getSingleTriple(RDFUri rdfUri, SearchTerm searchTerm, Map<String, String> searchValues,
            String numStr) {
        Triple t1 = new Triple(Var.alloc("id"), NodeFactory.createURI(rdfUri.uri()), Var.alloc(numStr));
        searchValues.put(numStr, searchTerm.getValue());
        return Arrays.asList(t1);
    }

    /**
     * Get an element that does not care what the field type is
     * 
     * @param searchTerm The search term 
     * @param searchValues The values to generate filters with
     * @param numStr The id number to associate with triples
     * @return The generated element
     */
    private Element getAllElement(SearchTerm searchTerm, Map<String, String> searchValues, String numStr) {
        // We create the group and add the filter directly here as otherwise the query takes too long.  Having the filter
        // where the unions are makes the query a lot faster

        ElementGroup group = new ElementGroup();
        ElementUnion union = new ElementUnion();
        union.addElement(getDirectFieldsElement(numStr, searchTerm.getValue()));
        union.addElement(getChildFieldsElement(numStr, searchTerm.getValue()));
        union.addElement(getAssociatedPeopleElement(numStr, searchTerm.getValue()));
        group.addElement(union);
        //group.addElementFilter(getCaseInsensitiveRegexFilter(numStr, searchTerm.getValue()));
        return group;
    }

    /**
     * Get a list of triples to represent a search based on a set item 
     * 
     * @param rdfUri The RDFUri annotation associated with the method found
     * @param searchTerm The search term
     * @param searchValues The values to generate filters with
     * @param numStr The id number to associate with triples
     * @param set The set to generate triples from
     * @return The generated triples
     */
    private List<Triple> getSetTriples(RDFUri rdfUri, SearchTerm searchTerm, Map<String, String> searchValues,
            String numStr, RDFSet set) {
        String groupIdStr = numStr + "_0";
        Var groupIdVar = Var.alloc(groupIdStr);

        List<Triple> triples = new ArrayList<Triple>();
        Triple triple = new Triple(Var.alloc("id"), NodeFactory.createURI(set.uri()), groupIdVar);
        triples.add(triple);

        triple = new Triple(groupIdVar, NodeFactory.createURI(rdfUri.uri()), Var.alloc(numStr));
        triples.add(triple);
        searchValues.put(numStr, searchTerm.getValue());

        return triples;
    }

    /**
     * Get a list of triples to represent a search based on a default set item
     * 
     * @param rdfUri The RDFUri annotation associated with the method found
     * @param searchTerm The search term
     * @param searchValues The values to generate filters with
     * @param numStr The id number to associate with triples
     * @param set The set to generate triples from
     * @return The generated triples
     */
    private List<Triple> getDefaultSetTriples(RDFUri rdfUri, SearchTerm searchTerm,
            Map<String, String> searchValues, String numStr, RDFSetWithDefault set) {
        RDFDefaultTriple[] tripleDefaults = set.defaults();
        String groupIdStr = numStr + "_0";
        List<Triple> triples = new ArrayList<Triple>();
        Var groupIdVar = Var.alloc(groupIdStr);
        Triple triple = new Triple(Var.alloc("id"), NodeFactory.createURI(set.uri()), groupIdVar);
        triples.add(triple);
        for (RDFDefaultTriple tripleDefault : tripleDefaults) {
            triple = new Triple(groupIdVar, NodeFactory.createURI(tripleDefault.predicate()),
                    NodeFactory.createLiteral(tripleDefault.object()));
            triples.add(triple);
        }
        triple = new Triple(groupIdVar, NodeFactory.createURI(rdfUri.uri()), Var.alloc(numStr));
        triples.add(triple);
        searchValues.put(numStr, searchTerm.getValue());

        return triples;
    }

    /**
     * Get triples based on a query for a subject
     * 
     * @param method The method associated with the subject
     * @param subjMethod The method associated with a field in the subject
     * @param searchTerm The search term
     * @param numStr The id number to associate with triples
     * @param set The set to generate triples from
     * @return The generated triples
     */
    private List<Triple> getSubjectTriples(Method method, Method subjMethod, SearchTerm searchTerm,
            Map<String, String> searchValues, String numStr) {
        List<Triple> triples = new ArrayList<Triple>();

        RDFUri subjUri = method.getAnnotation(RDFUri.class);
        RDFUri subjMethUri = subjMethod.getAnnotation(RDFUri.class);
        Triple subjTriple = new Triple(Var.alloc("id"), NodeFactory.createURI(subjUri.uri()),
                Var.alloc(numStr + "_1"));
        Triple methSubjTriple = new Triple(Var.alloc(numStr + "_1"), NodeFactory.createURI(subjMethUri.uri()),
                Var.alloc(numStr));
        triples.add(subjTriple);
        triples.add(methSubjTriple);
        searchValues.put(numStr, searchTerm.getValue());

        return triples;
    }
}