Esempio n. 1
0
bool MojDbIndex::canAnswer(const MojDbQuery& query) const
{
    LOG_TRACE("Entering function %s", __FUNCTION__);
	MojAssert(isOpen());
	MojAssert(!m_propNames.empty());

	// if this index is not ready yet, return false
	if (!m_ready)
		return false;

	// The goal here is to figure out whether the results for the query are contiguous in
	// the index. That will be the case if all the props referenced are contiguous in our
	// prop vector, any prop referenced by an inequality op comes at the end of the
	// range of referenced props, any unreferenced props in our vector are at the end,
	// and the order prop is either the last referenced prop or, if all ops are equality ops,
	// the prop following the last referenced prop.
	const MojDbQuery::WhereMap& map = query.where();
	MojSize numQueryProps = map.size();
	// if there are more props in the query than in our index, no can do
	if (numQueryProps > m_propNames.size())
		return false;
	// skip the first prop if we have an incDel index and the query does not reference the del prop
	StringVec::ConstIterator propName = m_propNames.begin();
	PropVec::ConstIterator prop = m_props.begin();
	if (m_includeDeleted && !map.contains(MojDb::DelKey)) {
		++propName;
		++prop;
	}
	// if there are no props in query, but the order matches our first prop, we're good
	const MojString& orderProp = query.order();
	if (numQueryProps == 0) {
		if (orderProp.empty()) {
			return isIdIndex();
		} else {
			return *propName == orderProp;
		}
	}
	// check all remaining props
	for (; propName != m_propNames.end(); ++propName, ++prop) {
		--numQueryProps;
		// ensure that the current prop is referenced in the query (no gaps)
		MojDbQuery::WhereMap::ConstIterator mapIter = map.find(*propName);
		if (mapIter == map.end())
			return false;
		// ensure that if it is an inequality op, it is at the end
		if (numQueryProps > 0 && mapIter->lowerOp() != MojDbQuery::OpEq)
			return false;
		// ensure that collation matches
		if (mapIter->collation() != (*prop)->collation())
			return false;
		// hooray, we made it through all the props in the query without failing any tests.
		// there may still be unreferenced props at the end of the vector, but that's ok.
		if (numQueryProps == 0) {
			// make sure that we can satisfy the ordering. if there is no ordering, or we are
			// ordering on a referenced prop, we're golden
			if (!orderProp.empty() && !map.contains(orderProp)) {
				// ensure that the order prop is next in our vec
				StringVec::ConstIterator next = propName + 1;
				if (next == m_propNames.end() || *next != orderProp)
					return false;
			}
			// can do!
			return true;
		}
	}
	return false;
}
Esempio n. 2
0
MojErr MojDbQueryPlan::buildRanges(const MojDbIndex& index)
{
	MojAssert(!index.props().empty());

	MojErr err = MojErrNone;
	const MojDbQuery::WhereMap& where = m_query.where();
	const StringVec& props = index.props();
	const MojDbQuery::WhereClause* lastClause = NULL;
	MojDbKeyBuilder lowerBuilder;
	MojDbKeyBuilder upperBuilder;
	MojDbKeyBuilder::KeySet prefixKeys;

	err = pushVal(lowerBuilder, index.id(), NULL);
	MojErrCheck(err);
	err = pushVal(upperBuilder, index.id(), NULL);
	MojErrCheck(err);

	// push vals for all props in index prop order to create upper and lower bounds
	for (StringVec::ConstIterator i = props.begin(); i != props.end(); ++i) {
		// get clause for prop
		MojDbQuery::WhereMap::ConstIterator clause = where.find(*i);
		if (clause == where.end()) {
			MojAssert((MojSize)(i - props.begin()) == where.size());
			break;
		}

		// create collator
		MojRefCountedPtr<MojDbTextCollator> collator;
		MojDbCollationStrength coll = clause->collation();
		if (coll != MojDbCollationInvalid) {
			collator.reset(new MojDbTextCollator);
			MojAllocCheck(collator.get());
			err = collator->init(m_locale, coll);
			MojErrCheck(err);
		}

		// push vals for clause
		MojDbQuery::CompOp lowerOp = clause->lowerOp();
		if (lowerOp == MojDbQuery::OpSearch) {
			// tokenize search strings
			err = pushSearch(lowerBuilder, upperBuilder, clause->lowerVal(), collator.get());
			MojErrCheck(err);
		} else {
			// copy off prefix before writing an inequality val
			if (lowerOp != MojDbQuery::OpEq && lowerOp != MojDbQuery::OpPrefix) {
				err = lowerBuilder.keys(prefixKeys);
				MojErrCheck(err);
				if (prefixKeys.empty()) {
					// if there is no prefix yet, put an empty key
					err = prefixKeys.put(MojDbKey());
					MojErrCheck(err);
				}
			}
			// append lower-value to lower-key
			err = pushVal(lowerBuilder, clause->lowerVal(), collator.get());
			MojErrCheck(err);
			// append appropriate value to upper-key
			if (clause->upperOp() == MojDbQuery::OpNone) {
				err = pushVal(upperBuilder, clause->lowerVal(), collator.get());
				MojErrCheck(err);
			} else {
				err = pushVal(upperBuilder, clause->upperVal(), collator.get());
				MojErrCheck(err);
			}
		}
		lastClause = &clause.value();
	}

	// go from builders to ranges
	KeySet lowerKeys;
	err = lowerBuilder.keys(lowerKeys);
	MojErrCheck(err);
	KeySet upperKeys;
	err = upperBuilder.keys(upperKeys);
	MojErrCheck(err);
	if (prefixKeys.empty()) {
		prefixKeys = lowerKeys;
	}
	err = rangesFromKeySets(lowerKeys, upperKeys, prefixKeys, lastClause);
	MojErrCheck(err);

	return MojErrNone;
}