The classfiles are downloadable on
http://www.vpro.nl/MMObjectNode.java and
http://www.vpro.nl/MMObjectBuilder.java
Seems that attachments on my side are discarded
-------------------------------------------------------------
Hi,
This bug is mentioned on butracker
MMBase has a performance-issue regarding MMObjectNode::getRelated() and
MMObjectNode::getRelated(type) where type is a builder.
Imagine a person who has 20.000 relations with userratings and 1 relation with an
image. If I where to ask mmbase to get the person.getRelated(Images), the following
will happen:
1.) the builder of type 'type' (image) will be fetched from mmbase
2.) mmbase gets *all* related nodes (20.001 in this example)
3.) it will check the otype of the node against the otype of the builder from step 1
4.) returns 1 image
There are 3 major problems with this:
- if you use inherited builders this current method will not work. If I ask
getRelated(Mediaparts)
the current implementation will return Mediaparts instead of the correct
Audio/Videoparts
- the nodecache will be filled with useless information (it will now contain the
20.001 nodes
from the getRelated in the previous example; the current nodecache is only 16.000
orso,
so a getRelated(type) on a node with many relations will invalidate the whole
cache and is worthless
at this point
- to determine the otype of the node, the relationnumber is being queried on the
object table field otype,
after that the builder for that otype will do a 'SELECT * from buildername' to
fetch the real values
for the node.. This will result in 40.002 queries on the database for just one (1)
image!
My current proposed sollution for getRelated(type) works as follows:
- query with a multilevel from this_node.parent -> type, this will return virtual
nodes (contain otype and number)
- check wether the node is in the nodecache, if so, return that, otherwise remember
the number
- query the builder in 1 search the uncached nodes, fetch those and fill in the real
values (this will put the
nodes automatically into the cache
- returns the nodes
This solves the inheritance-problem, queries the remaining nodes in 1 extra query and
fill the nodecache.
To get the previsous example, the related image-node, if not already in cache, will be
retreived in 2 queries
instead of 40.002..
The code is backwards-compatible. The only case in which it is not, are inherited
builders, which only Rob is
currently using.
There is one point of discussion, which is the Multilevelcache to speed things up a
bit more.If i have time,
I will try to incorporate that one too.
I have included the code-snip and the proposed sollution.You can switch with a boolean
between the old
and new implementation to be sure that if, for a reason I cannot image, it should not
work, can be activated
or deactivated. Look for the static boolean 'NEW_RELATED_TEST'.
gr,
marcel maatkamp
VPRO Digitaal
---
MMObjectNode
public Vector getRelatedNodes() {
Vector results = 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
if( builder != null ) {
// 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);
results = new Vector(getRealNodes(v, type));
} else {
log.error("This type("+type+") is not a valid buildername!");
}
return results;
}
/**
* 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 results = 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());
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);
results = new Vector(getRealNodes(v, type));
} else {
log.error("This type("+type+") is not a valid buildername!");
}
return results;
}
/**
* 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");
// same type, construct list further
if(ootype == -1 || otype == ootype) {
// convert type.number and type.otype to number and otype
convert = new MMObjectNode();
// parent needs to be set or else mmbase does nag nag nag on a
setValue()
convert.parent =
parent.mmb.getMMObject(parent.mmb.TypeDef.getValue(otype));
convert.setValue("number", node.getValue(type + ".number"));
convert.setValue("otype", otype);
list.add(convert);
// new strain detected, fetch list, add to result and make new list for
new parent
} else {
result.addAll(getRealNodesFromBuilder(list, ootype));
list = new ArrayList();
list.add(node);
ootype = otype;
}
// first and only list or last list, return real values
if(!e.hasMoreElements())
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;
}
MMObjectBuilder
----------------------
/**
* Convert virtual nodes to real nodes based on their otype
*
* Normally a multirelations-search will return virtual nodes. These nodes
* will only contain values which where specified in the field-vector.
* This method will make real nodes of those virtual nodes.
*
* @param List containing virtual nodes
* @return List containing real nodes
*/
public List getNodes(List virtuals) {
List result = new ArrayList();
MMObjectNode node = null;
Integer number = null;
String numbers = "";
Iterator i = virtuals.iterator();
while(i.hasNext()) {
node = (MMObjectNode)i.next();
number = new Integer(node.getIntValue("number"));
// check if this node is already in cache
if(nodeCache.containsKey(number))
result.add(nodeCache.get(number));
// else seek it with a search on builder in db
else
numbers += "" + number;
if(i.hasNext())
numbers += ",";
}
// now that we have a comma seperated string of numbers, we can
// the search with a where-clause containing this list
if(!numbers.equals("")) {
result = new ArrayList(basicSearch("SELECT * FROM "+getFullTableName()+"
WHERE number in ("+numbers+")"));
} // else everything from cache
// 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
virtualsnodes("+virtuals.size()+") to realnodes("+result.size()+")");
}
return result;
}