
org.datanucleus.store.neo4j.query.LazyLoadQueryResult Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of datanucleus-neo4j Show documentation
Show all versions of datanucleus-neo4j Show documentation
Plugin providing persistence to Neo4j graph datastores.
The newest version!
/**********************************************************************
Copyright (c) 2012 Andy Jefferson and others. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Contributors:
...
**********************************************************************/
package org.datanucleus.store.neo4j.query;
import java.io.ObjectStreamException;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.NoSuchElementException;
import org.datanucleus.ExecutionContext;
import org.datanucleus.metadata.AbstractClassMetaData;
import org.datanucleus.store.neo4j.Neo4jUtils;
import org.datanucleus.store.query.AbstractQueryResult;
import org.datanucleus.store.query.AbstractQueryResultIterator;
import org.datanucleus.store.query.Query;
import org.datanucleus.util.Localiser;
import org.datanucleus.util.NucleusLogger;
import org.datanucleus.util.SoftValueMap;
import org.datanucleus.util.StringUtils;
import org.datanucleus.util.WeakValueMap;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.graphdb.Result;
/**
* QueryResult for Neo4j queries that tries to lazy load results from the provided ExecutionResult
* so to avoid problems with memory. By default if the query is for instances of a candidate (i.e no result
* clause) then the method used to calculate the size is by doing a Cypher "count" query, and if the query is
* for a result clause then the method used to calculate the size is by loading all results; obviously the user
* can set it through a query extension/hint.
*
* TODO Support fetching objects in batches
*/
public class LazyLoadQueryResult extends AbstractQueryResult
{
protected ExecutionContext ec;
protected Result result;
protected String candidateAliasName;
protected AbstractClassMetaData cmd;
protected String[] cypherResults;
/** Map of object, keyed by the index (0, 1, etc). */
protected Map itemsByIndex = null;
public LazyLoadQueryResult(Query q, Result result, String cypherResult)
{
super(q);
this.candidateAliasName = query.getCompilation().getCandidateAlias();
this.ec = q.getExecutionContext();
this.cmd = ec.getMetaDataManager().getMetaDataForClass(query.getCandidateClass(), ec.getClassLoaderResolver());
this.result = result;
this.cypherResults = (cypherResult != null ? cypherResult.split(",") : null);
if (cypherResults == null || (candidateAliasName != null && cypherResults[0].equals(candidateAliasName)))
{
resultSizeMethod = "count";
}
else
{
resultSizeMethod = "last";
}
resultSizeMethod = "last"; // Override the above til we have confidence in count
// Process any supported extensions
String cacheType = query.getStringExtensionProperty("cacheType", "strong");
if (cacheType != null)
{
if (cacheType.equalsIgnoreCase("soft"))
{
itemsByIndex = new SoftValueMap();
}
else if (cacheType.equalsIgnoreCase("weak"))
{
itemsByIndex = new WeakValueMap();
}
else if (cacheType.equalsIgnoreCase("strong"))
{
itemsByIndex = new HashMap();
}
else if (cacheType.equalsIgnoreCase("none"))
{
itemsByIndex = null;
}
else
{
itemsByIndex = new WeakValueMap();
}
}
else
{
itemsByIndex = new WeakValueMap();
}
}
/* (non-Javadoc)
* @see org.datanucleus.store.query.AbstractQueryResult#closingConnection()
*/
@Override
protected void closingConnection()
{
resultSizeMethod = "last";
if (loadResultsAtCommit && isOpen() && result != null)
{
// Query connection closing message
NucleusLogger.QUERY.info(Localiser.msg("052606", query.toString()));
synchronized (this)
{
// Go through to end of Iterator
while (result.hasNext())
{
Map map = result.next();
Object result = getResultFromMapRow(map);
itemsByIndex.put(itemsByIndex.size(), result);
}
result.close();
result = null;
}
}
}
private Object getResultFromMapRow(Map map)
{
Object result = null;
if (cypherResults == null || (candidateAliasName != null && cypherResults[0].equals(candidateAliasName)))
{
// Candidate result
PropertyContainer node = (PropertyContainer) map.get(candidateAliasName);
AbstractClassMetaData propObjCmd = Neo4jUtils.getClassMetaDataForPropertyContainer(node, query.getExecutionContext(), cmd);
result = Neo4jUtils.getObjectForPropertyContainer(node, propObjCmd, query.getExecutionContext(), query.getIgnoreCache());
}
else
{
// Result clause specified, so extract into Object or Object[]
if (cypherResults.length == 1)
{
result = map.get(cypherResults[0]);
}
else
{
result = new Object[cypherResults.length];
for (int i=0;i map = result.next();
Object rowResult = getResultFromMapRow(map);
itemsByIndex.put(itemsByIndex.size(), rowResult);
if (!result.hasNext())
{
// Reached end of results, so null the iterator to signify this
result.close();
result = null;
}
return rowResult;
}
/* (non-Javadoc)
* @see org.datanucleus.store.query.AbstractQueryResult#iterator()
*/
@Override
public Iterator iterator()
{
return new QueryResultIterator();
}
/* (non-Javadoc)
* @see org.datanucleus.store.query.AbstractQueryResult#listIterator()
*/
@Override
public ListIterator listIterator()
{
return new QueryResultIterator();
}
private class QueryResultIterator extends AbstractQueryResultIterator
{
private int nextRowNum = 0;
@Override
public boolean hasNext()
{
synchronized (LazyLoadQueryResult.this)
{
if (!isOpen())
{
// Spec 14.6.7 Calling hasNext() on closed Query will return false
return false;
}
if (nextRowNum < itemsByIndex.size())
{
return true;
}
return (result != null && result.hasNext());
}
}
@Override
public Object next()
{
synchronized (LazyLoadQueryResult.this)
{
if (!isOpen())
{
// Spec 14.6.7 Calling next() on closed Query will throw NoSuchElementException
throw new NoSuchElementException(Localiser.msg("052600"));
}
if (nextRowNum < itemsByIndex.size())
{
// Already read in this value so return it
Object pojo = itemsByIndex.get(nextRowNum);
++nextRowNum;
return pojo;
}
else if (result != null && result.hasNext())
{
// Get next value from resultIterator
Object pojo = getNextObject();
++nextRowNum;
return pojo;
}
throw new NoSuchElementException(Localiser.msg("052602"));
}
}
@Override
public boolean hasPrevious()
{
// We only navigate in forward direction, but maybe could provide this method
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public int nextIndex()
{
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public Object previous()
{
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public int previousIndex()
{
throw new UnsupportedOperationException("Not yet implemented");
}
}
/* (non-Javadoc)
* @see org.datanucleus.store.query.AbstractQueryResult#equals(java.lang.Object)
*/
@Override
public boolean equals(Object o)
{
if (o == null || !(o instanceof LazyLoadQueryResult))
{
return false;
}
LazyLoadQueryResult other = (LazyLoadQueryResult)o;
// TODO Base equality on the results too (itemsByIndex) but means we have to load them
if (query != null)
{
return other.query == query;
}
return StringUtils.toJVMIDString(other).equals(StringUtils.toJVMIDString(this));
}
/**
* Handle serialisation by returning a java.util.ArrayList of all of the results for this query
* after disconnecting the query which has the consequence of enforcing the load of all objects.
* @return The object to serialise
* @throws ObjectStreamException Thrown if an error occurs
*/
protected Object writeReplace() throws ObjectStreamException
{
disconnect();
List list = new ArrayList();
for (int i=0;i
© 2015 - 2025 Weber Informatics LLC | Privacy Policy