mirror of https://gitee.com/bigwinds/arangodb
Merge branch 'devel' of github.com:arangodb/ArangoDB into feature/upgrade-v8
This commit is contained in:
commit
e06191d8ad
|
@ -3233,12 +3233,6 @@ struct SortToIndexNode final : public WalkerWorker<ExecutionNode> {
|
|||
// sorted GatherNode in the cluster
|
||||
indexNode->needsGatherNodeSort(true);
|
||||
_modified = true;
|
||||
} else if (numCovered > 0 && sortCondition.isUnidirectional()) {
|
||||
// remove the first few attributes if they are constant
|
||||
SortNode* sortNode =
|
||||
ExecutionNode::castTo<SortNode*>(_plan->getNodeById(_sortNode->id()));
|
||||
sortNode->removeConditions(numCovered);
|
||||
_modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,157 +0,0 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2018 ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Jan Christoph Uhde
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef ARANGOD_AQL_SINGLE_BLOCK_FETCHER_H
|
||||
#define ARANGOD_AQL_SINGLE_BLOCK_FETCHER_H
|
||||
|
||||
#include "Aql/AqlItemBlock.h"
|
||||
#include "Aql/AqlItemMatrix.h"
|
||||
#include "Aql/DependencyProxy.h"
|
||||
#include "Aql/ExecutionState.h"
|
||||
#include "Aql/InputAqlItemRow.h"
|
||||
#include "Aql/SortExecutor.h"
|
||||
|
||||
#include <Basics/Exceptions.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace arangodb {
|
||||
namespace aql {
|
||||
|
||||
class AqlItemBlock;
|
||||
template <BlockPassthrough>
|
||||
class DependencyProxy;
|
||||
|
||||
/**
|
||||
* @brief Interface for all AqlExecutors that do need all
|
||||
* rows at a time in order to make progress.
|
||||
*/
|
||||
template <BlockPassthrough pass>
|
||||
class SingleBlockFetcher {
|
||||
public:
|
||||
explicit SingleBlockFetcher(DependencyProxy<pass>& executionBlock)
|
||||
: _prefetched(false),
|
||||
_dependencyProxy(&executionBlock),
|
||||
_currentBlock(nullptr),
|
||||
_upstreamState(ExecutionState::HASMORE) {}
|
||||
|
||||
TEST_VIRTUAL ~SingleBlockFetcher() = default;
|
||||
|
||||
protected:
|
||||
// only for testing! Does not initialize _dependencyProxy!
|
||||
SingleBlockFetcher() = default;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Fetch one new AqlItemRow from upstream.
|
||||
* **Guarantee**: the pointer returned is valid only
|
||||
* until the next call to fetchRow.
|
||||
*
|
||||
* @return A pair with the following properties:
|
||||
* ExecutionState:
|
||||
* WAITING => IO going on, immediatly return to caller.
|
||||
* DONE => No more to expect from Upstream, if you are done with
|
||||
* this row return DONE to caller.
|
||||
* HASMORE => There is potentially more from above, call again if
|
||||
* you need more input.
|
||||
* AqlItemRow:
|
||||
* If WAITING => Do not use this Row, it is a nullptr.
|
||||
* If HASMORE => The Row is guaranteed to not be a nullptr.
|
||||
* If DONE => Row can be a nullptr (nothing received) or valid.
|
||||
*/
|
||||
|
||||
// SingleBlockFetcher cannot pass through. Could be implemented, but currently
|
||||
// there are no executors that could use this and not better use
|
||||
// SingleRowFetcher instead.
|
||||
|
||||
std::pair<ExecutionState, SharedAqlItemBlockPtr> fetchBlock(
|
||||
std::size_t limit = ExecutionBlock::DefaultBatchSize(), bool prefetch = false) {
|
||||
if (_prefetched) {
|
||||
TRI_ASSERT(!prefetch);
|
||||
_prefetched = false;
|
||||
return {_upstreamState, _currentBlock};
|
||||
}
|
||||
|
||||
if (_upstreamState == ExecutionState::DONE) {
|
||||
TRI_ASSERT(_currentBlock == nullptr);
|
||||
return {_upstreamState, _currentBlock};
|
||||
}
|
||||
|
||||
auto res = _dependencyProxy->fetchBlock(limit);
|
||||
_upstreamState = res.first;
|
||||
_currentBlock = res.second;
|
||||
|
||||
if (prefetch && _currentBlock != nullptr) {
|
||||
_prefetched = prefetch;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::pair<ExecutionState, SharedAqlItemBlockPtr> fetchBlockForModificationExecutor(
|
||||
std::size_t limit = ExecutionBlock::DefaultBatchSize()) {
|
||||
return fetchBlock(limit);
|
||||
}
|
||||
|
||||
std::pair<ExecutionState, SharedAqlItemBlockPtr> fetchBlockForPassthrough(size_t) {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_NOT_IMPLEMENTED);
|
||||
};
|
||||
|
||||
std::pair<ExecutionState, std::size_t> preFetchNumberOfRows(std::size_t) {
|
||||
fetchBlock(true);
|
||||
return {_upstreamState, _currentBlock != nullptr ? _currentBlock->size() : 0};
|
||||
}
|
||||
|
||||
InputAqlItemRow accessRow(std::size_t index) {
|
||||
TRI_ASSERT(_currentBlock != nullptr);
|
||||
TRI_ASSERT(index < _currentBlock->size());
|
||||
return InputAqlItemRow{_currentBlock, index};
|
||||
}
|
||||
|
||||
ExecutionState upstreamState() const { return _upstreamState; }
|
||||
SharedAqlItemBlockPtr currentBlock() const { return _currentBlock; }
|
||||
|
||||
// on purpose not implemented, this Fetcher is about to be removed
|
||||
// NOLINTNEXTLINE google-default-arguments
|
||||
std::pair<ExecutionState, ShadowAqlItemRow> fetchShadowRow(size_t atMost = 1) const {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
bool _prefetched;
|
||||
|
||||
private:
|
||||
DependencyProxy<pass>* _dependencyProxy;
|
||||
SharedAqlItemBlockPtr _currentBlock;
|
||||
ExecutionState _upstreamState;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Delegates to ExecutionBlock::getNrInputRegisters()
|
||||
*/
|
||||
RegisterId getNrInputRegisters() const {
|
||||
return _dependencyProxy->getNrInputRegisters();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace aql
|
||||
} // namespace arangodb
|
||||
#endif // ARANGOD_AQL_SINGLE_BLOCK_FETCHER_H
|
|
@ -189,25 +189,21 @@ size_t SortCondition::coveredAttributes(
|
|||
}
|
||||
|
||||
// no match
|
||||
bool isConstant = false;
|
||||
|
||||
if (isContained(indexAttributes, field.attributes) &&
|
||||
isContained(_constAttributes, field.attributes)) {
|
||||
// no field match, but a constant attribute
|
||||
isConstant = true;
|
||||
++fieldsPosition;
|
||||
++numCovered;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isConstant && isContained(_constAttributes, indexAttributes[i])) {
|
||||
if (isContained(_constAttributes, indexAttributes[i])) {
|
||||
// no field match, but a constant attribute
|
||||
isConstant = true;
|
||||
++i; // next index field
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isConstant) {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
TRI_ASSERT(numCovered <= _fields.size());
|
||||
|
|
|
@ -1908,9 +1908,88 @@ arangodb::Result fromFuncMinMatch(irs::boolean_filter* filter, QueryContext cons
|
|||
return {};
|
||||
}
|
||||
|
||||
|
||||
arangodb::Result processPhraseArgs(
|
||||
irs::by_phrase* phrase, QueryContext const& ctx,
|
||||
FilterContext const& filterCtx, arangodb::aql::AstNode const& valueArgs,
|
||||
size_t valueArgsBegin, size_t valueArgsEnd, irs::analysis::analyzer::ptr& analyzer,
|
||||
size_t offset, bool allowDefaultOffset, bool allowRecursion) {
|
||||
irs::string_ref value;
|
||||
bool expectingOffset = false;
|
||||
for (size_t idx = valueArgsBegin; idx < valueArgsEnd; ++idx) {
|
||||
auto currentArg = valueArgs.getMemberUnchecked(idx);
|
||||
if (!currentArg) {
|
||||
auto message = "'PHRASE' AQL function: Unable to parse argument on position "s + std::to_string(idx);
|
||||
LOG_TOPIC("44bed", WARN, arangodb::iresearch::TOPIC) << message;
|
||||
return { TRI_ERROR_BAD_PARAMETER, message };
|
||||
}
|
||||
if (currentArg->isArray() && (!expectingOffset || allowDefaultOffset)) {
|
||||
// array arg is processed with possible default 0 offsets - to be easily compatible with TOKENS function
|
||||
// No array recursion allowed. This could be allowed, but just looks tangled.
|
||||
// Anyone interested coud use FLATTEN to explicitly require processing all recurring arrays as one array
|
||||
if (allowRecursion) {
|
||||
auto subRes = processPhraseArgs(phrase, ctx, filterCtx, *currentArg, 0, currentArg->numMembers(), analyzer, offset, true, false);
|
||||
if (subRes.fail()) {
|
||||
return subRes;
|
||||
}
|
||||
expectingOffset = true;
|
||||
offset = 0;
|
||||
continue;
|
||||
} else {
|
||||
auto message = "'PHRASE' AQL function: recursive arrays not allowed at position "s + std::to_string(idx);
|
||||
LOG_TOPIC("66c24", WARN, arangodb::iresearch::TOPIC) << message;
|
||||
return { TRI_ERROR_BAD_PARAMETER, message };
|
||||
}
|
||||
}
|
||||
ScopedAqlValue currentValue(*currentArg);
|
||||
if (phrase || currentValue.isConstant()) {
|
||||
if (!currentValue.execute(ctx)) {
|
||||
auto message = "'PHRASE' AQL function: Unable to parse argument on position " + std::to_string(idx);
|
||||
LOG_TOPIC("d819d", WARN, arangodb::iresearch::TOPIC) << message;
|
||||
return { TRI_ERROR_BAD_PARAMETER, message };
|
||||
}
|
||||
if (arangodb::iresearch::SCOPED_VALUE_TYPE_DOUBLE == currentValue.type() && expectingOffset) {
|
||||
offset = static_cast<uint64_t>(currentValue.getInt64());
|
||||
expectingOffset = false;
|
||||
continue; // got offset let`s go search for value
|
||||
} else if ( (arangodb::iresearch::SCOPED_VALUE_TYPE_STRING != currentValue.type() || !currentValue.getString(value)) || // value is not a string at all
|
||||
(expectingOffset && !allowDefaultOffset)) { // offset is expected mandatory but got value
|
||||
std::string expectedValue;
|
||||
if (expectingOffset && allowDefaultOffset) {
|
||||
expectedValue = " as a value or offset";
|
||||
} else if (expectingOffset) {
|
||||
expectedValue = " as an offset";
|
||||
} else {
|
||||
expectedValue = " as a value";
|
||||
}
|
||||
auto message = "'PHRASE' AQL function: Unable to parse argument on position " + std::to_string(idx) + expectedValue;
|
||||
LOG_TOPIC("ac06b", WARN, arangodb::iresearch::TOPIC) << message;
|
||||
return { TRI_ERROR_BAD_PARAMETER, message };
|
||||
}
|
||||
} else {
|
||||
// in case of non const node encountered while parsing we can not decide if current and following args are correct before execution
|
||||
// so at this stage we say all is ok
|
||||
return {};
|
||||
}
|
||||
if (phrase) {
|
||||
TRI_ASSERT(analyzer);
|
||||
appendTerms(*phrase, value, *analyzer, offset);
|
||||
}
|
||||
offset = 0;
|
||||
expectingOffset = true;
|
||||
}
|
||||
if (!expectingOffset) { // that means last arg is numeric - this is error as no term to apply offset to
|
||||
auto message = "'PHRASE' AQL function : Unable to parse argument on position " + std::to_string(valueArgsEnd - 1) + "as a value"s;
|
||||
LOG_TOPIC("5fafe", WARN, arangodb::iresearch::TOPIC) << message;
|
||||
return { TRI_ERROR_BAD_PARAMETER, message };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
// note: <value> could be either string ether array of strings with offsets inbetween . Inside array
|
||||
// 0 offset could be omitted e.g. [term1, term2, 2, term3] is equal to: [term1, 0, term2, 2, term3]
|
||||
// PHRASE(<attribute>, <value> [, <offset>, <value>, ...] [, <analyzer>])
|
||||
// PHRASE(<attribute>, '[' <value> [, <offset>, <value>, ...] ']' [,
|
||||
// <analyzer>])
|
||||
// PHRASE(<attribute>, '[' <value> [, <offset>, <value>, ...] ']' [,<analyzer>])
|
||||
arangodb::Result fromFuncPhrase(irs::boolean_filter* filter, QueryContext const& ctx,
|
||||
FilterContext const& filterCtx, arangodb::aql::AstNode const& args) {
|
||||
if (!args.isDeterministic()) {
|
||||
|
@ -1940,7 +2019,7 @@ arangodb::Result fromFuncPhrase(irs::boolean_filter* filter, QueryContext const&
|
|||
ctx, argc, "PHRASE");
|
||||
|
||||
if (!analyzerPool._pool) {
|
||||
return {TRI_ERROR_INTERNAL};
|
||||
return {TRI_ERROR_BAD_PARAMETER};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1958,70 +2037,15 @@ arangodb::Result fromFuncPhrase(irs::boolean_filter* filter, QueryContext const&
|
|||
}
|
||||
|
||||
// ...........................................................................
|
||||
// 2nd argument defines a value
|
||||
// 2nd argument and later defines a values
|
||||
// ...........................................................................
|
||||
|
||||
auto const* valueArg = args.getMemberUnchecked(1);
|
||||
|
||||
if (!valueArg) {
|
||||
auto message = "'PHRASE' AQL function: 2nd argument is invalid";
|
||||
LOG_TOPIC("c3aec", WARN, arangodb::iresearch::TOPIC) << message;
|
||||
return {TRI_ERROR_BAD_PARAMETER, message};
|
||||
}
|
||||
|
||||
auto* valueArgs = &args;
|
||||
size_t valueArgsBegin = 1;
|
||||
size_t valueArgsEnd = argc;
|
||||
|
||||
if (valueArg->isArray()) {
|
||||
valueArgs = valueArg;
|
||||
valueArgsBegin = 0;
|
||||
valueArgsEnd = valueArg->numMembers();
|
||||
|
||||
if (0 == (valueArgsEnd & 1)) {
|
||||
auto message = "'PHRASE' AQL function: 2nd argument has an invalid number of members (must be an odd number)";
|
||||
LOG_TOPIC("05c0c", WARN, arangodb::iresearch::TOPIC) << message;
|
||||
return {TRI_ERROR_BAD_PARAMETER, message};
|
||||
}
|
||||
|
||||
valueArg = valueArgs->getMemberUnchecked(valueArgsBegin);
|
||||
|
||||
if (!valueArg) {
|
||||
std::stringstream ss;;
|
||||
ss << valueArg;
|
||||
auto message = "'PHRASE' AQL function: 2nd argument has an invalid member at offset: "s + ss.str();
|
||||
LOG_TOPIC("892bc", WARN, arangodb::iresearch::TOPIC) << message;
|
||||
return {TRI_ERROR_BAD_PARAMETER, message};
|
||||
}
|
||||
}
|
||||
|
||||
irs::string_ref value;
|
||||
ScopedAqlValue inputValue(*valueArg);
|
||||
|
||||
if (filter || inputValue.isConstant()) {
|
||||
if (!inputValue.execute(ctx)) {
|
||||
auto message = "'PHRASE' AQL function: Failed to evaluate 2nd argument";
|
||||
LOG_TOPIC("14a81", WARN, arangodb::iresearch::TOPIC) << message;
|
||||
return {TRI_ERROR_BAD_PARAMETER, message};
|
||||
}
|
||||
|
||||
if (arangodb::iresearch::SCOPED_VALUE_TYPE_STRING != inputValue.type()) {
|
||||
auto message = "'PHRASE' AQL function: 2nd argument has invalid type '"s +
|
||||
ScopedAqlValue::typeString(inputValue.type()).c_str() + "' (string expected)";
|
||||
LOG_TOPIC("a91b6", WARN, arangodb::iresearch::TOPIC) << message;
|
||||
return {TRI_ERROR_BAD_PARAMETER, message};
|
||||
}
|
||||
|
||||
if (!inputValue.getString(value)) {
|
||||
auto message = "'PHRASE' AQL function: Unable to parse 2nd argument as string";
|
||||
LOG_TOPIC("b546d", WARN, arangodb::iresearch::TOPIC) << message;
|
||||
return {TRI_ERROR_BAD_PARAMETER, message};
|
||||
}
|
||||
}
|
||||
|
||||
irs::by_phrase* phrase = nullptr;
|
||||
irs::analysis::analyzer::ptr analyzer;
|
||||
|
||||
// prepare filter if execution phase
|
||||
if (filter) {
|
||||
std::string name;
|
||||
|
||||
|
@ -2032,7 +2056,7 @@ arangodb::Result fromFuncPhrase(irs::boolean_filter* filter, QueryContext const&
|
|||
}
|
||||
|
||||
TRI_ASSERT(analyzerPool._pool);
|
||||
analyzer = analyzerPool._pool->get(); // get analyzer from pool
|
||||
analyzer = analyzerPool._pool->get();
|
||||
|
||||
if (!analyzer) {
|
||||
auto message = "'PHRASE' AQL function: Unable to instantiate analyzer '"s + analyzerPool._pool->name() + "'";
|
||||
|
@ -2045,63 +2069,10 @@ arangodb::Result fromFuncPhrase(irs::boolean_filter* filter, QueryContext const&
|
|||
phrase = &filter->add<irs::by_phrase>();
|
||||
phrase->field(std::move(name));
|
||||
phrase->boost(filterCtx.boost);
|
||||
|
||||
TRI_ASSERT(analyzer);
|
||||
appendTerms(*phrase, value, *analyzer, 0);
|
||||
}
|
||||
|
||||
decltype(fieldArg) offsetArg = nullptr;
|
||||
size_t offset = 0;
|
||||
|
||||
for (size_t idx = valueArgsBegin + 1, end = valueArgsEnd; idx < end; idx += 2) {
|
||||
offsetArg = valueArgs->getMemberUnchecked(idx);
|
||||
|
||||
if (!offsetArg) {
|
||||
auto message = "'PHRASE' AQL function: Unable to parse argument on position "s + std::to_string(idx) + " as an offset"s;
|
||||
LOG_TOPIC("44bed", WARN, arangodb::iresearch::TOPIC) << message;
|
||||
return {TRI_ERROR_BAD_PARAMETER, message};
|
||||
}
|
||||
|
||||
valueArg = valueArgs->getMemberUnchecked(idx + 1);
|
||||
|
||||
if (!valueArg) {
|
||||
auto message = "'PHRASE' AQL function: Unable to parse argument on position " + std::to_string(idx + 1) + " as a value";
|
||||
LOG_TOPIC("ac06b", WARN, arangodb::iresearch::TOPIC) << message;
|
||||
return {TRI_ERROR_BAD_PARAMETER, message};
|
||||
}
|
||||
|
||||
ScopedAqlValue offsetValue(*offsetArg);
|
||||
|
||||
if (filter || offsetValue.isConstant()) {
|
||||
if (!offsetValue.execute(ctx) ||
|
||||
arangodb::iresearch::SCOPED_VALUE_TYPE_DOUBLE != offsetValue.type()) {
|
||||
auto message = "'PHRASE' AQL function: Unable to parse argument on position " + std::to_string(idx) + " as an offset";
|
||||
LOG_TOPIC("d819d", WARN, arangodb::iresearch::TOPIC) << message;
|
||||
return {TRI_ERROR_BAD_PARAMETER, message};
|
||||
}
|
||||
|
||||
offset = static_cast<uint64_t>(offsetValue.getInt64());
|
||||
}
|
||||
|
||||
ScopedAqlValue inputValue(*valueArg);
|
||||
|
||||
if (filter || inputValue.isConstant()) {
|
||||
if (!inputValue.execute(ctx) ||
|
||||
arangodb::iresearch::SCOPED_VALUE_TYPE_STRING != inputValue.type() ||
|
||||
!inputValue.getString(value)) {
|
||||
auto message = "'PHRASE' AQL function: Unable to parse argument on position " + std::to_string(idx + 1) + " as a value";
|
||||
LOG_TOPIC("39e12", WARN, arangodb::iresearch::TOPIC) << message;
|
||||
return {TRI_ERROR_BAD_PARAMETER, message};
|
||||
}
|
||||
}
|
||||
|
||||
if (phrase) {
|
||||
TRI_ASSERT(analyzer);
|
||||
appendTerms(*phrase, value, *analyzer, offset);
|
||||
}
|
||||
}
|
||||
|
||||
return { }; //ok;
|
||||
// on top level we require explicit offsets - to be backward compatible and be able to distinguish last argument as analyzer or value
|
||||
// Also we allow recursion inside array to support older syntax (one array arg) and add ability to pass several arrays as args
|
||||
return processPhraseArgs(phrase, ctx, filterCtx, *valueArgs, valueArgsBegin, valueArgsEnd, analyzer, 0, false, true);
|
||||
}
|
||||
|
||||
// STARTS_WITH(<attribute>, <prefix>, [<scoring-limit>])
|
||||
|
|
|
@ -2609,10 +2609,6 @@ TEST_F(IResearchFilterFunctionTest, Phrase) {
|
|||
"FOR d IN myView FILTER AnalYZER(phrase(d.name, 'quick', d.name, "
|
||||
"'brown'), 'test_analyzer') RETURN d",
|
||||
&ExpressionContextMock::EMPTY);
|
||||
assertFilterFail(
|
||||
vocbase(),
|
||||
"FOR d IN myView FILTER AnaLYZER(phrase(d.name, [ 'quick', '0', "
|
||||
"'brown' ]), 'test_analyzer') RETURN d");
|
||||
assertFilterFail(
|
||||
vocbase(),
|
||||
"FOR d IN myView FILTER AnaLYZER(phrase(d.name, [ 'quick', null, "
|
||||
|
@ -2632,6 +2628,24 @@ TEST_F(IResearchFilterFunctionTest, Phrase) {
|
|||
&ExpressionContextMock::EMPTY);
|
||||
}
|
||||
|
||||
{
|
||||
irs::Or expected;
|
||||
auto& phrase = expected.add<irs::by_phrase>();
|
||||
phrase.field(mangleString("name", "test_analyzer"));
|
||||
phrase.push_back("q").push_back("u").push_back("i").push_back("c").push_back(
|
||||
"k").push_back("0");
|
||||
phrase.push_back("b").push_back("r").push_back("o").push_back("w").push_back(
|
||||
"n");
|
||||
assertFilterSuccess(
|
||||
vocbase(),
|
||||
"FOR d IN myView FILTER AnaLYZER(phrase(d.name, [ 'quick', '0', "
|
||||
"'brown' ]), 'test_analyzer') RETURN d", expected);
|
||||
assertFilterFail(
|
||||
vocbase(),
|
||||
"FOR d IN myView FILTER AnaLYZER(phrase(d.name, 'quick', '0', "
|
||||
"'brown'), 'test_analyzer') RETURN d");
|
||||
}
|
||||
|
||||
// with offset, complex name, custom analyzer
|
||||
// quick <...> <...> <...> <...> <...> brown
|
||||
{
|
||||
|
@ -3373,11 +3387,6 @@ TEST_F(IResearchFilterFunctionTest, Phrase) {
|
|||
"FOR d IN myView FILTER analyzer(phrase(d.obj.properties.id.name, "
|
||||
"'quick', 3, 'brown', false, 'fox', 0, 'jumps'), 'test_analyzer') "
|
||||
"RETURN d");
|
||||
assertFilterFail(
|
||||
vocbase(),
|
||||
"FOR d IN myView FILTER analyzer(phrase(d.obj.properties.id.name, [ "
|
||||
"'quick', 3, 'brown', '2', 'fox', 0, 'jumps' ]), 'test_analyzer') "
|
||||
"RETURN d");
|
||||
assertFilterFail(
|
||||
vocbase(),
|
||||
"FOR d IN myView FILTER analyzer(phrase(d.obj.properties.id.name, [ "
|
||||
|
@ -3561,6 +3570,55 @@ TEST_F(IResearchFilterFunctionTest, Phrase) {
|
|||
"'fox', 0.0, 'jumps' ], 'test_analyzer') RETURN d",
|
||||
expected, &ctx);
|
||||
}
|
||||
{
|
||||
irs::Or expected;
|
||||
auto& phrase = expected.add<irs::by_phrase>();
|
||||
phrase.field(mangleString("obj.properties.id.name", "test_analyzer"));
|
||||
phrase.push_back("q").push_back("u").push_back("i").push_back("c").push_back(
|
||||
"k");
|
||||
phrase.push_back("b", 3).push_back("r").push_back("o").push_back("w").push_back(
|
||||
"n");
|
||||
phrase.push_back("f").push_back("o").push_back("x");
|
||||
phrase.push_back("j").push_back("u").push_back("m").push_back("p").push_back(
|
||||
"s");
|
||||
|
||||
ExpressionContextMock ctx;
|
||||
ctx.vars.emplace("offset", arangodb::aql::AqlValue(arangodb::aql::AqlValueHintInt(2)));
|
||||
ctx.vars.emplace("input", arangodb::aql::AqlValue("bro"));
|
||||
|
||||
// implicit zero offsets
|
||||
assertFilterSuccess(
|
||||
vocbase(),
|
||||
"LET offset=2 LET input='bro' FOR d IN myView FILTER "
|
||||
"analyzer(phrase(d.obj.properties.id.name, ['quick', offset+1, "
|
||||
"CONCAT(input, 'wn'), 'fox', 'jumps']), 'test_analyzer') "
|
||||
"RETURN d",
|
||||
expected, &ctx);
|
||||
|
||||
// explicit zero offsets on top level
|
||||
assertFilterSuccess(
|
||||
vocbase(),
|
||||
"LET offset=2 LET input='bro' FOR d IN myView FILTER "
|
||||
"analyzer(phrase(d.obj.properties.id.name, ['quick'], offset+1, "
|
||||
"CONCAT(input, 'wn'), 0, ['f', 'o', 'x'], 0, ['j', 'u', 'mps']), 'test_analyzer') "
|
||||
"RETURN d",
|
||||
expected, &ctx);
|
||||
|
||||
// recurring arrays not allowed
|
||||
assertFilterFail(
|
||||
vocbase(),
|
||||
"FOR d IN myView FILTER "
|
||||
"analyzer(phrase(d.obj.properties.id.name, ['quick'], 3, "
|
||||
"'123', 'wn', 0, ['f', 'o', 'x'], 0, ['j', ['u'], 'mps']), 'test_analyzer') "
|
||||
"RETURN d", &ctx);
|
||||
|
||||
assertFilterFail(
|
||||
vocbase(),
|
||||
"FOR d IN myView FILTER "
|
||||
"analyzer(phrase(d.obj.properties.id.name, ['quick', 3, "
|
||||
"'123', 'wn', 0, 'f', 'o', 'x', 0, ['j'], 'u', 'mps']), 'test_analyzer') "
|
||||
"RETURN d", &ctx);
|
||||
}
|
||||
|
||||
// multiple offsets, complex name, custom analyzer, invalid expressions
|
||||
// quick <...> <...> <...> brown <...> <...> fox jumps
|
||||
|
@ -3601,13 +3659,6 @@ TEST_F(IResearchFilterFunctionTest, Phrase) {
|
|||
"TO_BOOL(offset+1), CONCAT(input, 'wn'), offset, 'fox', 0, 'jumps' ]), "
|
||||
"'test_analyzer') RETURN d",
|
||||
&ctx);
|
||||
assertFilterExecutionFail(
|
||||
vocbase(),
|
||||
"LET offset=2 LET input='bro' FOR d IN myView FILTER "
|
||||
"analyzer(phrase(d.obj.properties.id.name, [ 'quick', offset + 1.5, "
|
||||
"'brown', TO_STRING(2), 'fox', 0, 'jumps' ]), 'test_analyzer') RETURN "
|
||||
"d",
|
||||
&ctx);
|
||||
assertFilterExecutionFail(
|
||||
vocbase(),
|
||||
"LET offset=2 LET input='bro' FOR d IN myView FILTER "
|
||||
|
@ -3652,12 +3703,6 @@ TEST_F(IResearchFilterFunctionTest, Phrase) {
|
|||
"CONCAT(input, 'wn'), offset, 'fox', 0, 'jumps' ], 'test_analyzer') "
|
||||
"RETURN d",
|
||||
&ctx);
|
||||
assertFilterExecutionFail(
|
||||
vocbase(),
|
||||
"LET offset=2 LET input='bro' FOR d IN myView FILTER "
|
||||
"phrase(d.obj.properties.id.name, [ 'quick', offset + 1.5, 'brown', "
|
||||
"TO_STRING(2), 'fox', 0, 'jumps' ], 'test_analyzer') RETURN d",
|
||||
&ctx);
|
||||
assertFilterExecutionFail(
|
||||
vocbase(),
|
||||
"LET offset=2 LET input='bro' FOR d IN myView FILTER "
|
||||
|
@ -3713,9 +3758,10 @@ TEST_F(IResearchFilterFunctionTest, Phrase) {
|
|||
vocbase(),
|
||||
"FOR d IN myView FILTER analyzer(phrase(d['name'], 'quick'), { \"a\": 7, "
|
||||
"\"b\": \"c\" }) RETURN d");
|
||||
assertFilterFail(vocbase(),
|
||||
"FOR d IN myView FILTER analyzer(phrase(d.name, 'quick'), "
|
||||
"'invalid_analyzer') RETURN d");
|
||||
assertFilterFail(
|
||||
vocbase(),
|
||||
"FOR d IN myView FILTER analyzer(phrase(d.name, 'quick'), "
|
||||
"'invalid_analyzer') RETURN d");
|
||||
assertFilterFail(
|
||||
vocbase(),
|
||||
"FOR d IN myView FILTER analyzer(phrase(d['name'], 'quick'), "
|
||||
|
@ -3848,9 +3894,10 @@ TEST_F(IResearchFilterFunctionTest, Phrase) {
|
|||
vocbase(),
|
||||
"FOR d IN myView FILTER phrase(d.name, [ 'quick' ], 'invalid_analyzer') "
|
||||
"RETURN d");
|
||||
assertFilterFail(vocbase(),
|
||||
"FOR d IN myView FILTER phrase(d['name'], [ 'quick' ], "
|
||||
"'invalid_analyzer') RETURN d");
|
||||
assertFilterFail(
|
||||
vocbase(),
|
||||
"FOR d IN myView FILTER phrase(d['name'], [ 'quick' ], "
|
||||
"'invalid_analyzer') RETURN d");
|
||||
|
||||
// wrong analylzer
|
||||
assertFilterFail(
|
||||
|
@ -3883,9 +3930,10 @@ TEST_F(IResearchFilterFunctionTest, Phrase) {
|
|||
vocbase(),
|
||||
"FOR d IN myView FILTER analyzer(phrase(d.name, 'quick'), null) RETURN "
|
||||
"d");
|
||||
assertFilterFail(vocbase(),
|
||||
"FOR d IN myView FILTER analyzer(phrase(d.name, 'quick'), "
|
||||
"'invalidAnalyzer') RETURN d");
|
||||
assertFilterFail(
|
||||
vocbase(),
|
||||
"FOR d IN myView FILTER analyzer(phrase(d.name, 'quick'), "
|
||||
"'invalidAnalyzer') RETURN d");
|
||||
assertFilterExecutionFail(
|
||||
vocbase(),
|
||||
"FOR d IN myView FILTER analyzer(phrase(d.name, 'quick', 3, 'brown'), d) "
|
||||
|
|
|
@ -1301,6 +1301,79 @@ TEST_F(IResearchQueryPhraseTest, test) {
|
|||
ASSERT_TRUE(result.result.is(TRI_ERROR_BAD_PARAMETER));
|
||||
}
|
||||
|
||||
// test invalid input type (invalid order of terms)
|
||||
{
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH PHRASE(d['value'], 1, '12312', '12313') SORT BM25(d) ASC, TFIDF(d) DESC, d.seq RETURN d");
|
||||
ASSERT_TRUE(result.result.is(TRI_ERROR_BAD_PARAMETER));
|
||||
}
|
||||
|
||||
// test invalid input type (invalid order of terms 2)
|
||||
{
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH PHRASE(d['value'], '12312', '12313', 2 ) SORT BM25(d) ASC, TFIDF(d) DESC, d.seq RETURN d");
|
||||
ASSERT_TRUE(result.result.is(TRI_ERROR_BAD_PARAMETER));
|
||||
}
|
||||
|
||||
// test invalid input type (invalid order of terms 3)
|
||||
{
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH PHRASE(d['value'], '12312', 2, 2, '12313') SORT BM25(d) ASC, TFIDF(d) DESC, d.seq RETURN d");
|
||||
ASSERT_TRUE(result.result.is(TRI_ERROR_BAD_PARAMETER));
|
||||
}
|
||||
|
||||
// test invalid input type (invalid order of terms) [] args
|
||||
{
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH PHRASE(d['value'], 1, ['12312'], ['12313']) SORT BM25(d) ASC, TFIDF(d) DESC, d.seq RETURN d");
|
||||
ASSERT_TRUE(result.result.is(TRI_ERROR_BAD_PARAMETER));
|
||||
}
|
||||
|
||||
// test invalid input type (invalid order of terms 2) [] args
|
||||
{
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH PHRASE(d['value'], ['12312'], ['12313'], 2 ) SORT BM25(d) ASC, TFIDF(d) DESC, d.seq RETURN d");
|
||||
ASSERT_TRUE(result.result.is(TRI_ERROR_BAD_PARAMETER));
|
||||
}
|
||||
|
||||
// test invalid input type (invalid order of terms 3) [] args
|
||||
{
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH PHRASE(d['value'], ['12312'], 2, 2, ['12313']) SORT BM25(d) ASC, TFIDF(d) DESC, d.seq RETURN d");
|
||||
ASSERT_TRUE(result.result.is(TRI_ERROR_BAD_PARAMETER));
|
||||
}
|
||||
|
||||
// test invalid input type (invalid order of terms) via []
|
||||
{
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH PHRASE(d['value'], [1, '12312', '12313']) SORT BM25(d) ASC, TFIDF(d) DESC, d.seq RETURN d");
|
||||
ASSERT_TRUE(result.result.is(TRI_ERROR_BAD_PARAMETER));
|
||||
}
|
||||
|
||||
// test invalid input type (invalid order of terms 2) via []
|
||||
{
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH PHRASE(d['value'], ['12312', '12313', 2] ) SORT BM25(d) ASC, TFIDF(d) DESC, d.seq RETURN d");
|
||||
ASSERT_TRUE(result.result.is(TRI_ERROR_BAD_PARAMETER));
|
||||
}
|
||||
|
||||
// test invalid input type (invalid order of terms 3) via []
|
||||
{
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH PHRASE(d['value'], ['12312', 2, 2, '12313']) SORT BM25(d) ASC, TFIDF(d) DESC, d.seq RETURN d");
|
||||
ASSERT_TRUE(result.result.is(TRI_ERROR_BAD_PARAMETER));
|
||||
}
|
||||
|
||||
|
||||
// test missing value
|
||||
{
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
|
@ -1928,8 +2001,8 @@ TEST_F(IResearchQueryPhraseTest, test) {
|
|||
std::vector<arangodb::velocypack::Slice> expected = {};
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH ANALYZER(PHRASE(d['duplicated'], [ 'v', 1, "
|
||||
"'c' ], 'test_analyzer'), 'identity') SORT BM25(d) ASC, TFIDF(d) DESC, "
|
||||
"FOR d IN testView SEARCH PHRASE(d['duplicated'], [ 'v', 1, "
|
||||
"'c' ], 'test_analyzer') SORT BM25(d) ASC, TFIDF(d) DESC, "
|
||||
"d.seq RETURN d");
|
||||
ASSERT_TRUE(result.result.ok());
|
||||
auto slice = result.data->slice();
|
||||
|
@ -1945,4 +2018,219 @@ TEST_F(IResearchQueryPhraseTest, test) {
|
|||
|
||||
EXPECT_EQ(i, expected.size());
|
||||
}
|
||||
// test custom analyzer with multiple mixed offsets
|
||||
{
|
||||
std::vector<arangodb::velocypack::Slice> expected = {
|
||||
insertedDocs[6].slice(), insertedDocs[10].slice(),
|
||||
insertedDocs[16].slice(), insertedDocs[26].slice(),
|
||||
insertedDocs[32].slice(), insertedDocs[36].slice()
|
||||
};
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH PHRASE(d.duplicated, ['a', 'b'], 1, ['d'], "
|
||||
"'test_analyzer') SORT BM25(d) ASC, TFIDF(d) DESC, d.seq RETURN d");
|
||||
ASSERT_TRUE(result.result.ok());
|
||||
auto slice = result.data->slice();
|
||||
EXPECT_TRUE(slice.isArray());
|
||||
size_t i = 0;
|
||||
|
||||
for (arangodb::velocypack::ArrayIterator itr(slice); itr.valid(); ++itr) {
|
||||
auto const resolved = itr.value().resolveExternals();
|
||||
EXPECT_TRUE(i < expected.size());
|
||||
EXPECT_TRUE((0 == arangodb::basics::VelocyPackHelper::compare(expected[i++],
|
||||
resolved, true)));
|
||||
}
|
||||
|
||||
EXPECT_EQ(i, expected.size());
|
||||
}
|
||||
// test custom analyzer with multiple mixed offsets via []
|
||||
{
|
||||
std::vector<arangodb::velocypack::Slice> expected = {
|
||||
insertedDocs[6].slice(), insertedDocs[10].slice(),
|
||||
insertedDocs[16].slice(), insertedDocs[26].slice(),
|
||||
insertedDocs[32].slice(), insertedDocs[36].slice()
|
||||
};
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH PHRASE(d.duplicated, ['a', 'b', 1, 'd'], "
|
||||
"'test_analyzer') SORT BM25(d) ASC, TFIDF(d) DESC, d.seq RETURN d");
|
||||
ASSERT_TRUE(result.result.ok());
|
||||
auto slice = result.data->slice();
|
||||
EXPECT_TRUE(slice.isArray());
|
||||
size_t i = 0;
|
||||
|
||||
for (arangodb::velocypack::ArrayIterator itr(slice); itr.valid(); ++itr) {
|
||||
auto const resolved = itr.value().resolveExternals();
|
||||
EXPECT_TRUE(i < expected.size());
|
||||
EXPECT_TRUE((0 == arangodb::basics::VelocyPackHelper::compare(expected[i++],
|
||||
resolved, true)));
|
||||
}
|
||||
|
||||
EXPECT_EQ(i, expected.size());
|
||||
}
|
||||
// test custom analyzer with multiple mixed offsets
|
||||
{
|
||||
std::vector<arangodb::velocypack::Slice> expected = {
|
||||
insertedDocs[6].slice(), insertedDocs[10].slice(),
|
||||
insertedDocs[16].slice(), insertedDocs[26].slice(),
|
||||
insertedDocs[32].slice(), insertedDocs[36].slice()
|
||||
};
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH PHRASE(d.duplicated, ['a', 1, 'c'], 0, 'd', "
|
||||
"'test_analyzer') SORT BM25(d) ASC, TFIDF(d) DESC, d.seq RETURN d");
|
||||
ASSERT_TRUE(result.result.ok());
|
||||
auto slice = result.data->slice();
|
||||
EXPECT_TRUE(slice.isArray());
|
||||
size_t i = 0;
|
||||
|
||||
for (arangodb::velocypack::ArrayIterator itr(slice); itr.valid(); ++itr) {
|
||||
auto const resolved = itr.value().resolveExternals();
|
||||
EXPECT_TRUE(i < expected.size());
|
||||
EXPECT_TRUE((0 == arangodb::basics::VelocyPackHelper::compare(expected[i++],
|
||||
resolved, true)));
|
||||
}
|
||||
|
||||
EXPECT_EQ(i, expected.size());
|
||||
}
|
||||
// test custom analyzer with multiple mixed offsets
|
||||
{
|
||||
std::vector<arangodb::velocypack::Slice> expected = {
|
||||
insertedDocs[6].slice(), insertedDocs[10].slice(),
|
||||
insertedDocs[16].slice(), insertedDocs[26].slice(),
|
||||
insertedDocs[32].slice(), insertedDocs[36].slice()
|
||||
};
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH PHRASE(d.duplicated, ['a', 'b', 'c'], 0, 'd', "
|
||||
"'test_analyzer') SORT BM25(d) ASC, TFIDF(d) DESC, d.seq RETURN d");
|
||||
ASSERT_TRUE(result.result.ok());
|
||||
auto slice = result.data->slice();
|
||||
EXPECT_TRUE(slice.isArray());
|
||||
size_t i = 0;
|
||||
|
||||
for (arangodb::velocypack::ArrayIterator itr(slice); itr.valid(); ++itr) {
|
||||
auto const resolved = itr.value().resolveExternals();
|
||||
EXPECT_TRUE(i < expected.size());
|
||||
EXPECT_TRUE((0 == arangodb::basics::VelocyPackHelper::compare(expected[i++],
|
||||
resolved, true)));
|
||||
}
|
||||
|
||||
EXPECT_EQ(i, expected.size());
|
||||
}
|
||||
// test custom analyzer with multiple mixed offsets via []
|
||||
{
|
||||
std::vector<arangodb::velocypack::Slice> expected = {
|
||||
insertedDocs[6].slice(), insertedDocs[10].slice(),
|
||||
insertedDocs[16].slice(), insertedDocs[26].slice(),
|
||||
insertedDocs[32].slice(), insertedDocs[36].slice()
|
||||
};
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH ANALYZER(PHRASE(d.duplicated, ['a', 1, 'c', 'd']), "
|
||||
"'test_analyzer') SORT BM25(d) ASC, TFIDF(d) DESC, d.seq RETURN d");
|
||||
ASSERT_TRUE(result.result.ok());
|
||||
auto slice = result.data->slice();
|
||||
EXPECT_TRUE(slice.isArray());
|
||||
size_t i = 0;
|
||||
|
||||
for (arangodb::velocypack::ArrayIterator itr(slice); itr.valid(); ++itr) {
|
||||
auto const resolved = itr.value().resolveExternals();
|
||||
EXPECT_TRUE(i < expected.size());
|
||||
EXPECT_TRUE((0 == arangodb::basics::VelocyPackHelper::compare(expected[i++],
|
||||
resolved, true)));
|
||||
}
|
||||
|
||||
EXPECT_EQ(i, expected.size());
|
||||
}
|
||||
// testarray at first arg
|
||||
{
|
||||
std::vector<arangodb::velocypack::Slice> expected = {
|
||||
insertedDocs[6].slice(), insertedDocs[10].slice(),
|
||||
insertedDocs[16].slice(), insertedDocs[26].slice(),
|
||||
insertedDocs[32].slice(), insertedDocs[36].slice()
|
||||
};
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH ANALYZER(PHRASE(d.duplicated, ['a', 1, 'c', 'd']), "
|
||||
"'test_analyzer') SORT BM25(d) ASC, TFIDF(d) DESC, d.seq RETURN d");
|
||||
ASSERT_TRUE(result.result.ok());
|
||||
auto slice = result.data->slice();
|
||||
EXPECT_TRUE(slice.isArray());
|
||||
size_t i = 0;
|
||||
|
||||
for (arangodb::velocypack::ArrayIterator itr(slice); itr.valid(); ++itr) {
|
||||
auto const resolved = itr.value().resolveExternals();
|
||||
EXPECT_TRUE(i < expected.size());
|
||||
EXPECT_TRUE((0 == arangodb::basics::VelocyPackHelper::compare(expected[i++],
|
||||
resolved, true)));
|
||||
}
|
||||
|
||||
EXPECT_EQ(i, expected.size());
|
||||
}
|
||||
// testarray at first arg with analyzer
|
||||
{
|
||||
std::vector<arangodb::velocypack::Slice> expected = {
|
||||
insertedDocs[6].slice(), insertedDocs[10].slice(),
|
||||
insertedDocs[16].slice(), insertedDocs[26].slice(),
|
||||
insertedDocs[32].slice(), insertedDocs[36].slice()
|
||||
};
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH PHRASE(d.duplicated, ['a', 1, 'c', 'd'], "
|
||||
"'test_analyzer') SORT BM25(d) ASC, TFIDF(d) DESC, d.seq RETURN d");
|
||||
ASSERT_TRUE(result.result.ok());
|
||||
auto slice = result.data->slice();
|
||||
EXPECT_TRUE(slice.isArray());
|
||||
size_t i = 0;
|
||||
|
||||
for (arangodb::velocypack::ArrayIterator itr(slice); itr.valid(); ++itr) {
|
||||
auto const resolved = itr.value().resolveExternals();
|
||||
EXPECT_TRUE(i < expected.size());
|
||||
EXPECT_TRUE((0 == arangodb::basics::VelocyPackHelper::compare(expected[i++],
|
||||
resolved, true)));
|
||||
}
|
||||
|
||||
EXPECT_EQ(i, expected.size());
|
||||
}
|
||||
// array recursion simple
|
||||
{
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH PHRASE(d.prefix, ['b', 1, ['t', 'e', 1, 'a']], "
|
||||
"'test_analyzer') SORT BM25(d) ASC, TFIDF(d) DESC, d.seq RETURN d");
|
||||
ASSERT_TRUE(result.result.is(TRI_ERROR_BAD_PARAMETER));
|
||||
}
|
||||
// array recursion
|
||||
{
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH PHRASE(d.prefix, ['b', 1, ['t', 'e', 1, 'a']], 0, ['d'], 0, ['s', 0, 'f', 's'], 1, [[['a', 1, 'd']]], 0, 'f', "
|
||||
"'test_analyzer') SORT BM25(d) ASC, TFIDF(d) DESC, d.seq RETURN d");
|
||||
ASSERT_TRUE(result.result.is(TRI_ERROR_BAD_PARAMETER));
|
||||
}
|
||||
// array recursion via []
|
||||
{
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH PHRASE(d.prefix, [['b', 1, ['t', 'e', 1, 'a']], 0, ['d'], 0, ['s', 0, 'f', 's'], 1, [[['a', 1, 'd']]], 0, 'f'], "
|
||||
"'test_analyzer') SORT BM25(d) ASC, TFIDF(d) DESC, d.seq RETURN d");
|
||||
ASSERT_TRUE(result.result.is(TRI_ERROR_BAD_PARAMETER));
|
||||
}
|
||||
// array recursion without analyzer
|
||||
{
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH ANALYZER(PHRASE(d.prefix, ['b', 1, ['t', 'e', 1, 'a']], 0, ['d'], 0, ['s', 0, 'f', 's'], 1, [[['a', 1, 'd']]], 0, 'f'), "
|
||||
"'test_analyzer') SORT BM25(d) ASC, TFIDF(d) DESC, d.seq RETURN d");
|
||||
ASSERT_TRUE(result.result.is(TRI_ERROR_BAD_PARAMETER));
|
||||
}
|
||||
// array recursion without analyzer via []
|
||||
{
|
||||
auto result = arangodb::tests::executeQuery(
|
||||
vocbase,
|
||||
"FOR d IN testView SEARCH ANALYZER(PHRASE(d.prefix, [['b', 1, ['t', 'e', 1, 'a']], 0, ['d'], 0, ['s', 0, 'f', 's'], 1, [[['a', 1, 'd']]], 0, 'f']), "
|
||||
"'test_analyzer') SORT BM25(d) ASC, TFIDF(d) DESC, d.seq RETURN d");
|
||||
ASSERT_TRUE(result.result.is(TRI_ERROR_BAD_PARAMETER));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -703,6 +703,7 @@ void assertFilter(TRI_vocbase_t& vocbase, bool parseOk, bool execOk,
|
|||
std::shared_ptr<arangodb::velocypack::Builder> bindVars /*= nullptr*/,
|
||||
std::string const& refName /*= "d"*/
|
||||
) {
|
||||
SCOPED_TRACE(testing::Message("assertFilter failed for query:<") << queryString << "> parseOk:" << parseOk << " execOk:" << execOk);
|
||||
auto options = std::make_shared<arangodb::velocypack::Builder>();
|
||||
|
||||
arangodb::aql::Query query(false, vocbase, arangodb::aql::QueryString(queryString),
|
||||
|
|
|
@ -470,6 +470,26 @@ function iResearchAqlTestSuite () {
|
|||
|
||||
assertEqual(result3.length, 1);
|
||||
assertEqual(result3[0].name, 'full');
|
||||
|
||||
var result4 = db._query("FOR doc IN UnitTestsView SEARCH ANALYZER(PHRASE(doc.text, 'quick ', 1, ' fox jumps'), 'text_en') OPTIONS { waitForSync : true } RETURN doc").toArray();
|
||||
|
||||
assertEqual(result4.length, 1);
|
||||
assertEqual(result4[0].name, 'full');
|
||||
|
||||
var result5 = db._query("FOR doc IN UnitTestsView SEARCH ANALYZER(PHRASE(doc.text, [ 'quick ', 1, ' fox jumps' ]), 'text_en') OPTIONS { waitForSync : true } RETURN doc").toArray();
|
||||
|
||||
assertEqual(result5.length, 1);
|
||||
assertEqual(result5[0].name, 'full');
|
||||
|
||||
var result6 = db._query("FOR doc IN UnitTestsView SEARCH ANALYZER(PHRASE(doc.text, 'quick ', 0, 'brown', 0, [' fox', ' jumps']), 'text_en') OPTIONS { waitForSync : true } RETURN doc").toArray();
|
||||
|
||||
assertEqual(result6.length, 1);
|
||||
assertEqual(result6[0].name, 'full');
|
||||
|
||||
var result7 = db._query("FOR doc IN UnitTestsView SEARCH ANALYZER(PHRASE(doc.text, [ 'quick ', 'brown', ' fox jumps' ]), 'text_en') OPTIONS { waitForSync : true } RETURN doc").toArray();
|
||||
|
||||
assertEqual(result7.length, 1);
|
||||
assertEqual(result7[0].name, 'full');
|
||||
},
|
||||
|
||||
testExistsFilter : function () {
|
||||
|
|
|
@ -196,17 +196,16 @@ function optimizerRuleTestSuite() {
|
|||
[ "FOR v IN " + colName + " FILTER v.b == 1 SORT v.b, v.a RETURN 1", false, true ],
|
||||
[ "FOR v IN " + colName + " FILTER v.a == 1 && v.b == 1 SORT v.b, v.a RETURN 1", true, false ],
|
||||
[ "FOR v IN " + colName + " FILTER v.a == 1 && v.b == 1 SORT v.a, v.b RETURN 1", true, false ],
|
||||
[ "FOR v IN " + colName + " FILTER v.a == 1 && v.b == 1 SORT v.a, v.c RETURN 1", true, true ],
|
||||
[ "FOR v IN " + colName + " FILTER v.a == 1 && v.b == 1 SORT v.a, v.c RETURN 1", false, true ],
|
||||
[ "FOR v IN " + colName + " FILTER v.a == 1 && v.b == 1 SORT v.b, v.a RETURN 1", true, false ],
|
||||
[ "FOR v IN " + colName + " FILTER v.a == 1 && v.b == 1 SORT v.a, v.b, v.c RETURN 1", true, true ]
|
||||
[ "FOR v IN " + colName + " FILTER v.a == 1 && v.b == 1 SORT v.a, v.b, v.c RETURN 1", false, true ]
|
||||
];
|
||||
|
||||
queries.forEach(function(query) {
|
||||
var result = AQL_EXPLAIN(query[0]);
|
||||
if (query[1]) {
|
||||
assertNotEqual(-1, removeAlwaysOnClusterRules(result.plan.rules).indexOf(ruleName), query[0]);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
assertEqual(-1, removeAlwaysOnClusterRules(result.plan.rules).indexOf(ruleName), query[0]);
|
||||
}
|
||||
if (query[2]) {
|
||||
|
@ -409,7 +408,6 @@ function optimizerRuleTestSuite() {
|
|||
// place since the index can't fullfill all of the sorting criteria.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
testSortMoreThanIndexed: function () {
|
||||
|
||||
var query = "FOR v IN " + colName + " FILTER v.a == 1 SORT v.a, v.c RETURN [v.a, v.b, v.c]";
|
||||
// no index can be used for v.c -> sort has to remain in place!
|
||||
var XPresult;
|
||||
|
@ -438,7 +436,7 @@ function optimizerRuleTestSuite() {
|
|||
QResults[2] = AQL_EXECUTE(query, { }, paramIndexFromSort_IndexRange).json;
|
||||
XPresult = AQL_EXPLAIN(query, { }, paramIndexFromSort_IndexRange);
|
||||
|
||||
assertEqual([ ruleName, secondRuleName ], removeAlwaysOnClusterRules(XPresult.plan.rules).sort());
|
||||
assertEqual([ secondRuleName ], removeAlwaysOnClusterRules(XPresult.plan.rules).sort());
|
||||
// The sortnode and its calculation node should not have been removed.
|
||||
hasSortNode(XPresult);
|
||||
hasCalculationNodes(XPresult, 4);
|
||||
|
@ -1193,7 +1191,7 @@ function optimizerRuleTestSuite() {
|
|||
testSortModifyFilterCondition : function () {
|
||||
var query = "FOR v IN " + colName + " FILTER v.a == 123 SORT v.a, v.xxx RETURN v";
|
||||
var rules = AQL_EXPLAIN(query).plan.rules;
|
||||
assertNotEqual(-1, rules.indexOf(ruleName));
|
||||
assertEqual(-1, rules.indexOf(ruleName));
|
||||
assertNotEqual(-1, rules.indexOf(secondRuleName));
|
||||
assertNotEqual(-1, rules.indexOf("remove-filter-covered-by-index"));
|
||||
|
||||
|
@ -1204,9 +1202,8 @@ function optimizerRuleTestSuite() {
|
|||
++seen;
|
||||
assertFalse(node.reverse);
|
||||
} else if (node.type === "SortNode") {
|
||||
// first sort condition (v.a) should have been removed because it is const
|
||||
++seen;
|
||||
assertEqual(1, node.elements.length);
|
||||
assertEqual(2, node.elements.length);
|
||||
}
|
||||
});
|
||||
assertEqual(2, seen);
|
||||
|
|
Loading…
Reference in New Issue