Proposal MMObjectNode.getRelatedNodes performance
I am working on a project for VPRO digitaal to create a server deployment
based on
the MMBase 1.6 final release. The VPRO has some modified org.mmbase.*
files which
should be synced with the current MMBase source files.
The MMObjectNode of the VPRO has a different implementation of the
getRelatedNodes
methods which are faster then the MMBase 1.6 release.
The current 1.6 implementation retrieves all the related nodes from the
database and
throws away the nodes which are not required. There are N+1 queries needed
to get the
related nodes from the database (N is the number of related nodes). This
is very slow
if the request needs only one related type and not all types. The mmbase
sources
uses the getRelatedNodes for one type more often then the one for all types.
The VPRO implementation retrieves the node number and type of all related
nodes. If
the requested type matches the node type or the type of an inherited
builder then
the node is retrieved from the database.
The proposal I want to make is to replace the mmbase source with the VPRO
implementation for the 1.6.1 release. The VPRO implementation runs now for
more
then 6 months without issues and the implementation doesn't have any database
specific code.
Let me know what you all think about this proposal to improve the
preformance of the
MMObjectNode. I will make a Vote for this based in the replies.
Nico Klasens
/**
* Get all related nodes. The returned nodes are not the
* nodes directly attached to this node (the relation nodes) but the nodes
* attached to the relation nodes of this node.
* @return a <code>Vector</code> containing <code>MMObjectNode</code>s
*/
public Vector getRelatedNodes() {
Vector result = new Vector();
String type ="object";
MMObjectBuilder builder =
(MMObjectBuilder)parent.mmb.getMMObject(type);
// example: we want a thisnode.relatedNodes(mediaparts) where
mediaparts
are of type
// audioparts and videoparts. This method will return the real nodes
(thus of type audio/videoparts)
// when asked to get nodes of type mediaparts.
//
// - get a list of virtual nodes from a multilevel("this.parent.name,
type") ordered on otype
// (this will return virtual audio- and/or videoparts ordered on
their
*real* parent)
// - construct a list of nodes for each parentbuilder seperately
// - ask the parentbuilder for each list of virtual nodes to get a list
of the real nodes
// 'object' is not a valid builder, but it is accepted in this query
if( builder != null || type.equals("object")) {
// multilevel from table this.parent.name -> type
Vector tables = new Vector();
tables.addElement(parent.getTableName());
tables.addElement(type);
// return type.number (and otype for sorting)
Vector fields = new Vector();
fields.addElement(type + ".number");
fields.addElement(type + ".otype");
// order list UP
Vector directions = new Vector();
directions.addElement("UP");
// and order on otype
Vector ordered = new Vector();
ordered.addElement(type + ".otype");
// retrieve the related nodes (these are virtual)
Vector v = multirelations.searchMultiLevelVector(
getNumber(),fields,"NO",tables,"",ordered,directions);
result = new Vector(getRealNodes(v, type));
} else {
log.error("This type("+type+") is not a valid buildername!");
}
log.debug("related("+parent.getTableName()+"("+getNumber()+")) =
size("+result.size()+")");
return result;
}
private Map makeMap(Vector v) {
HashMap result = new HashMap();
MMObjectNode node = null;
Iterator i = v.iterator();
while(i.hasNext()) {
node = (MMObjectNode)i.next();
result.put(node.getStringValue("number"), node);
}
return result;
}
private Map addOther(Map source, Map destin) {
Iterator i = destin.keySet().iterator();
String number = null;
while(i.hasNext()) {
number = (String)i.next();
if(!source.containsKey(number))
source.put(number, (MMObjectNode)destin.get(number));
}
return source;
}
/**
* Get the related nodes of a certain type. The returned nodes are not the
* nodes directly attached to this node (the relation nodes) but the nodes
* attached to the relation nodes of this node.
*
* @param type the type of objects to be returned
* @return a <code>Vector</code> containing <code>MMObjectNode</code>s
*/
public Vector getRelatedNodes(String type) {
Vector result = null;
if(parent.mmb.InsRel.usesdir) {
result = getRelatedNodes(type, ClusterBuilder.SEARCH_EITHER);
} else {
// determine related nodes
Map source = makeMap(getRelatedNodes(type,
ClusterBuilder.SEARCH_SOURCE));
Map destin = makeMap(getRelatedNodes(type,
ClusterBuilder.SEARCH_DESTINATION));
log.debug("source("+source.size()+") -
destin("+destin.size()+")");
// remove duplicates (can happen if multirel is being used
when no dir
on insrel exists)
result = new Vector(addOther(source,destin).values());
}
return result;
}
/**
* If you query from this_node_type(type) (typex, insrel, typey where
typex == typey) {
* if the insrel table is directional, use the multirelations.SEARCH_EITHER
* if the insrel table is not directional, use the
multirelations.SEARCH_SOURCE + multirelations.SEARCH_DESTINATION
* }
* Otherwise the SEARCH_EITHER will result in an OR on insrel which will
never return in
* (huge) databases.
*/
private Vector getRelatedNodes(String type, int search_type) {
Vector result = new Vector();
MMObjectBuilder builder =
(MMObjectBuilder)parent.mmb.getMMObject(type);
// example: we want a thisnode.relatedNodes(mediaparts) where
mediaparts
are of type
// audioparts and videoparts. This method will return the real nodes
(thus of type audio/videoparts)
// when asked to get nodes of type mediaparts.
//
// - get a list of virtual nodes from a multilevel("this.parent.name,
type") ordered on otype
// (this will return virtual audio- and/or videoparts ordered on
their
*real* parent)
// - construct a list of nodes for each parentbuilder seperately
// - ask the parentbuilder for each list of virtual nodes to get a list
of the real nodes
if( builder != null ) {
// multilevel from table this.parent.name -> type
Vector tables = new Vector();
tables.addElement(parent.getTableName()+"1");
tables.addElement(type+"2");
// return type.number (and otype for sorting)
Vector fields = new Vector();
fields.addElement(type + "2.number");
fields.addElement(type + "2.otype");
// order list UP
Vector directions = new Vector();
directions.addElement("UP");
// and order on otype
Vector ordered = new Vector();
ordered.addElement(type + "2.otype");
String where = "WHERE " + parent.getTableName() +"1.number !=
" + type
+ "2.number";
Vector vnode=new Vector();
vnode.addElement(""+getNumber());
// retrieve the related nodes (these are virtual)
Vector v = multirelations.searchMultiLevelVector(
vnode,fields,"NO",tables,where,ordered,directions,search_type);
if(v == null) v = new Vector();
result = new Vector(getRealNodes(v, type+"2"));
} else {
log.error("This type("+type+") is not a valid buildername!");
}
log.debug("related("+parent.getTableName()+"("+getNumber()+")) ->
"+type+" = size("+result.size()+")");
return result;
}
/**
* Loop through the virtuals vector, group all same nodes based on parent
and fetch the real nodes from those parents
*
* @param Vector of virtual nodes (only type.number and type.otype fields
are set)
* @param type, needed to retreive the otype, which is set in node as
type + ".otype"
* @returns List of real nodes
*
* @see getRelatedNodes(String type)
*/
private List getRealNodes(Vector virtuals, String type) {
List result = new ArrayList();
MMObjectBuilder rparent = null;
MMObjectNode node = null;
MMObjectNode convert = null;
Enumeration e = virtuals.elements();
List list = new ArrayList();
int otype = -1;
int ootype = -1;
// fill the list
while(e.hasMoreElements()) {
node = (MMObjectNode)e.nextElement();
otype = node.getIntValue(type + ".otype");
// convert the nodes of type ootype to real numbers
if(otype != ootype) {
// if we have nodes return real values
if(ootype != -1) {
result.addAll(getRealNodesFromBuilder(list,
ootype));
list = new ArrayList();
}
ootype = otype;
}
// convert current node type.number and type.otype to number
and otype
convert = new
MMObjectNode(parent.mmb.getMMObject(parent.mmb.TypeDef.getValue(otype)));
// parent needs to be set or else mmbase does nag nag nag on a
setValue()
convert.setValue("number", node.getValue(type + ".number"));
convert.setValue("otype", otype);
list.add(convert);
// first and only list or last list, return real values
if(!e.hasMoreElements()) {
// log.debug("subconverting last "+list.size()+" nodes
of
type("+otype+")");
result.addAll(getRealNodesFromBuilder(list, otype));
}
}
// check that we didnt loose any nodes
// Java 1.4
// assert(virtuals.size() == result.size());
// Below Java 1.4
if(virtuals.size() != result.size()) {
log.error("We lost a few nodes during conversion from
virtualnodes("+virtuals.size()+") to realnodes("+result.size()+")");
}
return result;
}
private List getRealNodesFromBuilder(List list, int otype) {
List result = new ArrayList();
String name = parent.mmb.TypeDef.getValue(otype);
if(name != null) {
MMObjectBuilder rparent = parent.mmb.getMMObject(name);
if(rparent != null) {
result.addAll(rparent.getNodes(list));
} else {
log.error("This otype("+otype+") does not denote a
valid
typedef-name("+name+")!");
}
} else {
log.error("This otype("+otype+") gives no name("+name+") from
typedef!");
}
return result;
}