package org.basex.core.cmd; import static org.basex.core.Text.*; import java.io.IOException; import org.basex.core.User; import org.basex.data.Data; import org.basex.data.MetaData; import org.basex.index.IndexToken.IndexType; import org.basex.util.Util; import org.basex.util.list.IntList; /** * Evaluates the 'optimize' command and optimizes the data structures of * the currently opened database. Indexes and statistics are refreshed, * which is especially helpful after updates. * * @author BaseX Team 2005-12, BSD License * @author Christian Gruen */ public final class Optimize extends ACreate { /** Current pre value. */ private int pre; /** Data size. */ private int size; /** * Default constructor. */ public Optimize() { super(DATAREF | User.WRITE); } @Override protected boolean run() { final Data d = context.data(); final MetaData m = d.meta; size = m.size; try { optimize(d, this); } catch(final IOException ex) { Util.debug(ex); } return info(DB_OPTIMIZED_X, m.name, perf); } @Override public double prog() { return (double) pre / size; } @Override public boolean stoppable() { return false; } @Override public String det() { return CREATE_STATS_D; } /** * Optimize data structures. * @param d data * @throws IOException I/O Exception during index rebuild */ public static void optimize(final Data d) throws IOException { optimize(d, null); } /** * Optimize data structures. * @param d data * @param c calling command (can be null) * @throws IOException I/O Exception during index rebuild */ private static void optimize(final Data d, final Optimize c) throws IOException { // refresh indexes d.paths.close(); d.resources.init(); d.tagindex.init(); d.atnindex.init(); final MetaData m = d.meta; m.dirty = true; final IntList pars = new IntList(); final IntList tags = new IntList(); int n = 0; for(int pre = 0; pre < m.size; ++pre) { final byte kind = (byte) d.kind(pre); final int par = d.parent(pre, kind); while(!pars.empty() && pars.peek() > par) { pars.pop(); tags.pop(); } final int level = pars.size(); if(kind == Data.DOC) { if(m.createpath) d.paths.index(0, kind, level); pars.push(pre); tags.push(0); ++n; } else if(kind == Data.ELEM) { final int id = d.name(pre); d.tagindex.index(d.tagindex.key(id), null, true); if(m.createpath) d.paths.index(id, kind, level); pars.push(pre); tags.push(id); } else if(kind == Data.ATTR) { final int id = d.name(pre); final byte[] val = d.text(pre, false); d.atnindex.index(d.atnindex.key(id), val, true); if(m.createpath) d.paths.index(id, kind, level, val, m); } else { final byte[] val = d.text(pre, true); if(kind == Data.TEXT && level > 1) { d.tagindex.index(tags.peek(), val); } if(m.createpath) d.paths.index(0, kind, level, val, m); } if(c != null) c.pre = pre; } m.ndocs = n; m.pathindex = m.createpath; m.uptodate = true; try { optimize(IndexType.ATTRIBUTE, d, m.createattr, c); optimize(IndexType.TEXT, d, m.createtext, c); optimize(IndexType.FULLTEXT, d, m.createftxt, c); } finally { d.flush(); } } /** * Optimizes the specified index. * @param type index type * @param d data reference * @param create create flag * @param c calling command * @throws IOException I/O exception * */ private static void optimize(final IndexType type, final Data d, final boolean create, final Optimize c) throws IOException { if(create) create(type, d, c); else drop(type, d); } }