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;
        }




Reply via email to