1
0
Fork 0
arangodb/3rdParty/iresearch/core/iql/query_builder.cpp

975 lines
30 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2016 by EMC Corporation, All Rights Reserved
///
/// 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 EMC Corporation
///
/// @author Andrey Abramov
/// @author Vasiliy Nabatchikov
////////////////////////////////////////////////////////////////////////////////
#include <sstream>
#include "parser_context.hpp"
#include "analysis/analyzers.hpp"
#include "analysis/token_attributes.hpp"
#include "analysis/token_stream.hpp"
#include "search/all_filter.hpp"
#include "search/boolean_filter.hpp"
#include "search/phrase_filter.hpp"
#include "search/range_filter.hpp"
#include "search/term_filter.hpp"
#include "utils/locale_utils.hpp"
#include "query_builder.hpp"
#if defined (__GNUC__)
#pragma GCC diagnostic push
#if (__GNUC__ >= 7)
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough=0"
#endif
#endif
NS_LOCAL
// -----------------------------------------------------------------------------
// --SECTION-- private members
// -----------------------------------------------------------------------------
// by default no transformation is performed and value is treated verbatim
const irs::iql::query_builder::branch_builder_function_t RANGE_EE_BRANCH_BUILDER =
[](
irs::iql::proxy_filter& root,
const std::locale& locale,
const iresearch::string_ref& field,
void* cookie,
const std::vector<irs::iql::function_arg>& args
)->bool {
iresearch::bstring minValue;
iresearch::bstring maxValue;
bool bMinValueNil;
bool bMaxValueNil;
if (args.size() != 2 ||
!args[0].value(minValue, bMinValueNil, locale, cookie) ||
!args[1].value(maxValue, bMaxValueNil, locale, cookie)) {
return false;
}
auto& range = root.proxy<iresearch::by_range>().field(field);
if (!bMinValueNil) {
range.term<iresearch::Bound::MIN>(std::move(minValue))
.include<iresearch::Bound::MIN>(false);
}
if (!bMaxValueNil) {
range.term<iresearch::Bound::MAX>(std::move(maxValue))
.include<iresearch::Bound::MAX>(false);
}
return true;
};
const irs::iql::query_builder::branch_builder_function_t RANGE_EI_BRANCH_BUILDER =
[](
irs::iql::proxy_filter& root,
const std::locale& locale,
const iresearch::string_ref& field,
void* cookie,
const std::vector<irs::iql::function_arg>& args
)->bool {
iresearch::bstring minValue;
iresearch::bstring maxValue;
bool bMinValueNil;
bool bMaxValueNil;
if (args.size() != 2 ||
!args[0].value(minValue, bMinValueNil, locale, cookie) ||
!args[1].value(maxValue, bMaxValueNil, locale, cookie)) {
return false;
}
auto& range = root.proxy<iresearch::by_range>().field(field);
if (!bMinValueNil) {
range.term<iresearch::Bound::MIN>(std::move(minValue))
.include<iresearch::Bound::MIN>(false);
}
if (!bMaxValueNil) {
range.term<iresearch::Bound::MAX>(std::move(maxValue))
.include<iresearch::Bound::MAX>(true);
}
return true;
};
const irs::iql::query_builder::branch_builder_function_t RANGE_IE_BRANCH_BUILDER =
[](
irs::iql::proxy_filter& root,
const std::locale& locale,
const iresearch::string_ref& field,
void* cookie,
const std::vector<irs::iql::function_arg>& args
)->bool {
iresearch::bstring minValue;
iresearch::bstring maxValue;
bool bMinValueNil;
bool bMaxValueNil;
if (args.size() != 2 ||
!args[0].value(minValue, bMinValueNil, locale, cookie) ||
!args[1].value(maxValue, bMaxValueNil, locale, cookie)) {
return false;
}
auto& range = root.proxy<iresearch::by_range>().field(field);
if (!bMinValueNil) {
range.term<iresearch::Bound::MIN>(std::move(minValue))
.include<iresearch::Bound::MIN>(true);
}
if (!bMaxValueNil) {
range.term<iresearch::Bound::MAX>(std::move(maxValue))
.include<iresearch::Bound::MAX>(false);
}
return true;
};
const irs::iql::query_builder::branch_builder_function_t RANGE_II_BRANCH_BUILDER =
[](
irs::iql::proxy_filter& root,
const std::locale& locale,
const iresearch::string_ref& field,
void* cookie,
const std::vector<irs::iql::function_arg>& args
)->bool {
iresearch::bstring minValue;
iresearch::bstring maxValue;
bool bMinValueNil;
bool bMaxValueNil;
if (args.size() != 2 ||
!args[0].value(minValue, bMinValueNil, locale, cookie) ||
!args[1].value(maxValue, bMaxValueNil, locale, cookie)) {
return false;
}
if (bMinValueNil && bMaxValueNil) {
// exact equivalence optimization for nil value
root.proxy<iresearch::by_term>().field(field).term(iresearch::bytes_ref::NIL);
}
else if (!bMinValueNil && !bMaxValueNil && minValue == maxValue) {
// exact equivalence optimization
root.proxy<iresearch::by_term>().field(field).term(std::move(minValue));
}
else {
auto& range = root.proxy<iresearch::by_range>().field(field);
if (!bMinValueNil) {
range.term<iresearch::Bound::MIN>(std::move(minValue))
.include<iresearch::Bound::MIN>(true);
}
if (!bMaxValueNil) {
range.term<iresearch::Bound::MAX>(std::move(maxValue))
.include<iresearch::Bound::MAX>(true);
}
}
return true;
};
const irs::iql::query_builder::branch_builder_function_t SIMILAR_BRANCH_BUILDER =
[](
irs::iql::proxy_filter& root,
const std::locale& locale,
const iresearch::string_ref& field,
void* cookie,
const std::vector<irs::iql::function_arg>& args
)->bool {
iresearch::bstring value;
bool bValueNil;
if (args.size() != 1 ||
!args[0].value(value, bValueNil, locale, cookie)) {
return false;
}
const iresearch::string_ref value_ref(bValueNil ? iresearch::string_ref::NIL : iresearch::ref_cast<char>(value));
auto tokens = irs::analysis::analyzers::get(
"text", irs::text_format::text, irs::locale_utils::name(locale)
);
if (!tokens || !tokens->reset(value_ref)) {
return false;
}
auto& node = root.proxy<iresearch::by_phrase>().field(field);
for (auto& term = tokens->attributes().get<iresearch::term_attribute>(); tokens->next();) {
node.push_back(term->value());
}
return true;
};
// -----------------------------------------------------------------------------
// --SECTION-- private functions
// -----------------------------------------------------------------------------
class parse_context;
class ErrorNode: public iresearch::filter {
public:
ErrorNode(): filter(ErrorNode::type()) {}
iresearch::filter::prepared::ptr prepare(
const iresearch::index_reader&,
const iresearch::order::prepared&,
boost_t,
const iresearch::attribute_view&) const override {
iresearch::filter::prepared::ptr result; // null-ptr result
return result;
}
private:
friend parse_context;
std::string sError;
DECLARE_FILTER_TYPE();
};
DEFINE_FILTER_TYPE(ErrorNode);
////////////////////////////////////////////////////////////////////////////////
/// @brief proxy_filter specialized for iresearch::filter::ptr
////////////////////////////////////////////////////////////////////////////////
class LinkNode: public irs::iql::proxy_filter_t<std::shared_ptr<iresearch::filter>> {
public:
LinkNode(iresearch::filter* link): proxy_filter_t(LinkNode::type()) {
filter_ = ptr(link);
}
LinkNode(const LinkNode& other): proxy_filter_t(LinkNode::type()) {
filter_ = other.filter_;
}
template<typename... Args>
static ptr make(Args&&... args) {
PTR_NAMED(LinkNode, ptr, std::forward<Args>(args)...);
return ptr;
}
private:
DECLARE_FILTER_TYPE();
};
DEFINE_FILTER_TYPE(LinkNode);
class RootNode: public iresearch::Or {
public:
DECLARE_FACTORY_DEFAULT();
private:
friend parse_context;
iresearch::order order;
size_t nLimit;
};
DEFINE_FACTORY_DEFAULT(RootNode);
class parse_context: public irs::iql::parser_context {
public:
parse_context(
const std::string& sQuery,
const std::locale& locale,
void* cookie,
const irs::iql::functions& functions,
const irs::iql::query_builder::branch_builders& branch_builders
);
irs::iql::query build();
irs::iql::query buildError();
private:
static const irs::iql::parser::semantic_type SUCCESS;
static const irs::iql::parser::semantic_type UNKNOWN;
const irs::iql::query_builder::branch_builders& m_branch_builders;
void* m_cookie;
const std::locale& m_locale;
irs::iql::parser::semantic_type append_function_arg(
irs::iql::function_arg::fn_args_t& buf,
const irs::iql::parser::semantic_type& node_id
) const; // @return SUCCESS or ID of failed node, or UNKNOWN for self
irs::iql::parser::semantic_type children(
iresearch::boolean_filter& node, const std::vector<size_t>& children
) const; // @return SUCCESS or ID of failed node, or UNKNOWN for self
irs::iql::parser::semantic_type eval(
iresearch::bstring& buf,
const query_node& src
) const; // @return SUCCESS or ID of failed node, or UNKNOWN for self
template <typename fn_type, typename... ctx_args_type>
irs::iql::parser::semantic_type eval(
typename fn_type::contextual_buffer_t& buf,
const typename fn_type::contextual_function_t& fn,
const std::vector<size_t>& args,
const ctx_args_type&... ctx_args
) const; // @return SUCCESS or ID of failed node, or UNKNOWN for self
query_node const& find_node(
irs::iql::parser::semantic_type const& value
) const {
// force visibility only of const fn for GCC, otherwise it tries private fn
return parser_context::find_node(value);
}
template <typename T> // @return SUCCESS or ID of failed node, or UNKNOWN for self
irs::iql::parser::semantic_type init(T& node, const query_node& src) const;
irs::iql::parser::semantic_type initRange(
irs::iql::proxy_filter& node,
const iresearch::string_ref& field,
const irs::iql::parser::semantic_type& min_node_id, bool min_inclusive,
const irs::iql::parser::semantic_type& max_value_id, bool max_inclusive
) const;
irs::iql::parser::semantic_type initSimilar(
irs::iql::proxy_filter& node,
const iresearch::string_ref& field,
const irs::iql::parser::semantic_type& value_id
) const;
irs::iql::parser::semantic_type order(
iresearch::order& node, const std::vector<std::pair<size_t, bool>>& order
) const; // @return SUCCESS or ID of failed node
};
const irs::iql::parser::semantic_type parse_context::SUCCESS =
std::numeric_limits<irs::iql::parser::semantic_type>::max(); // init() success
const irs::iql::parser::semantic_type parse_context::UNKNOWN =
std::numeric_limits<irs::iql::parser::semantic_type>::max() - 1; // init() unknown failure
parse_context::parse_context(
const std::string& sQuery,
const std::locale& locale,
void* cookie,
const irs::iql::functions& functions,
const irs::iql::query_builder::branch_builders& branch_builders
):
parser_context(sQuery, functions),
m_branch_builders(branch_builders),
m_cookie(cookie),
m_locale(locale) {
}
// add implementation before any calls to the function
template<typename fn_type, typename... ctx_args_type>
irs::iql::parser::semantic_type parse_context::eval(
typename fn_type::contextual_buffer_t& buf,
const typename fn_type::contextual_function_t& fn,
const std::vector<size_t>& args,
const ctx_args_type&... ctx_args
) const {
irs::iql::function_arg::fn_args_t fnArgs;
fnArgs.reserve(args.size());
// build function argument list
for (size_t i = 0, count = args.size(); i < count; ++i) {
auto errorNodeId = append_function_arg(fnArgs, args[i]);
if (SUCCESS != errorNodeId) {
return UNKNOWN == errorNodeId ? args[i] : errorNodeId;
}
}
return fn(buf, ctx_args..., fnArgs) ? SUCCESS : UNKNOWN;
}
template<> // add implementation before any calls to the function
irs::iql::parser::semantic_type parse_context::init<irs::iql::proxy_filter>(
irs::iql::proxy_filter& node, const query_node& src
) const {
assert(
(query_node::NodeType::FUNCTION == src.type && src.pFnBoolean) ||
query_node::NodeType::EQUAL == src.type ||
query_node::NodeType::LIKE == src.type
);
node.boost(src.fBoost);
if (query_node::NodeType::FUNCTION == src.type && src.pFnBoolean) {
return eval<irs::iql::boolean_function, std::locale, void*>(
node, function(*(src.pFnBoolean)), src.children, m_locale, m_cookie
);
}
assert(src.children.size() == 2); // 2 - left and right side of operator
auto& left = find_node(src.children[0]);
iresearch::bstring fieldBuf;
{
auto errorNodeId = eval(fieldBuf, left);
if (SUCCESS != errorNodeId) {
return UNKNOWN == errorNodeId ? src.children[0] : errorNodeId;
}
}
auto field = iresearch::ref_cast<char>(fieldBuf);
switch(src.type) {
case query_node::NodeType::EQUAL: {
// iresearch:iql::parser implementation returns range on right
auto& right = find_node(src.children[1]);
assert(query_node::NodeType::RANGE == right.type);
assert(right.children.size() == 2); // 2 - min and max value of range
auto errorNodeId = initRange(
node, field,
right.children[0], right.bBeginInclusive,
right.children[1], right.bEndInclusive
);
return UNKNOWN == errorNodeId ? src.children[1] : errorNodeId;
}
case query_node::NodeType::LIKE: {
auto errorNodeId = initSimilar(node, field, src.children[1]);
return UNKNOWN == errorNodeId ? src.children[1] : errorNodeId;
}
default: {} // NOOP
}
return UNKNOWN;
}
template<> // add implementation before any calls to the function
irs::iql::parser::semantic_type parse_context::init<iresearch::Or>(
iresearch::Or& node, const query_node& src
) const {
assert(query_node::NodeType::UNION == src.type);
node.boost(src.fBoost);
return children(node, src.children);
}
template<> // add implementation before any calls to the function
irs::iql::parser::semantic_type parse_context::init<iresearch::And>(
iresearch::And& node, const query_node& src
) const {
assert(query_node::NodeType::INTERSECTION == src.type);
node.boost(src.fBoost);
return children(node, src.children);
}
template<> // add implementation before any calls to the function
irs::iql::parser::semantic_type parse_context::init<iresearch::Not>(
iresearch::Not& node, const query_node& src
) const {
assert(
(query_node::NodeType::FUNCTION == src.type && src.pFnBoolean) ||
query_node::NodeType::EQUAL == src.type ||
query_node::NodeType::LIKE == src.type
);
return init(node.filter<irs::iql::proxy_filter>(), src);
}
template<> // add implementation before any calls to the function
irs::iql::parser::semantic_type parse_context::init<iresearch::all>(
iresearch::all& node, const query_node& src
) const {
assert(query_node::NodeType::BOOL_TRUE == src.type);
node.boost(src.fBoost);
return SUCCESS;
}
irs::iql::query parse_context::build() {
auto state = current_state();
irs::iql::query result;
if (!state.pnFilter) {
return buildError();
}
query_node root;
root.type = query_node::NodeType::UNION;
root.children.push_back(*state.pnFilter);
result.filter = RootNode::make();
auto errorNodeId =
init(*static_cast<iresearch::Or*>(result.filter.get()), root);
// initialize filter
if (SUCCESS != errorNodeId) {
std::stringstream error;
error << "filter conversion error, node: @" << errorNodeId << std::endl;
print(error, *state.pnFilter, false, true);
result.filter = ErrorNode::make<ErrorNode>();
result.error = &(static_cast<ErrorNode*>(result.filter.get())->sError);
*(result.error) = error.str();
return result;
}
// initialize order
if (!state.order.empty()) {
errorNodeId =
order(static_cast<RootNode*>(result.filter.get())->order, state.order);
if (SUCCESS != errorNodeId) {
std::string sDelim = "";
std::stringstream error;
error << "order conversion error, node: @" << errorNodeId << std::endl;
for (auto orderTerm: state.order) {
error << sDelim;
print(error, orderTerm.first, false, true);
error << (orderTerm.second ? " ASC": " DESC");
sDelim = ", ";
}
result.filter = ErrorNode::make<ErrorNode>();
result.error = &(static_cast<ErrorNode*>(result.filter.get())->sError);
*(result.error) = error.str();
return result;
}
result.order = &(static_cast<RootNode*>(result.filter.get())->order);
}
// initialize limit
if (state.pnLimit) {
result.limit = &(static_cast<RootNode*>(result.filter.get())->nLimit);
*(result.limit) = *(state.pnLimit);
}
return result;
}
irs::iql::query parse_context::buildError() {
auto state = current_state();
std::stringstream error;
error << "@";
if (!state.pError) {
error << "(" << state.nOffset << "): parse error";
}
else {
error << "("
<< "[" << state.pError->nStart
<< " - " << state.pError->nEnd << "], "
<< state.nOffset << "): "
<< state.pError->sMessage;
}
irs::iql::query result;
result.filter = ErrorNode::make<ErrorNode>();
result.error = &(static_cast<ErrorNode*>(result.filter.get())->sError);
*(result.error) = error.str();
return result;
}
irs::iql::parser::semantic_type parse_context::append_function_arg(
irs::iql::function_arg::fn_args_t& buf,
const irs::iql::parser::semantic_type& node_id
) const {
irs::iql::function_arg::fn_args_t fnArgs;
auto& node = find_node(node_id);
switch(node.type) {
case query_node::NodeType::UNION:
buf.emplace_back(
std::move(fnArgs),
[this, node_id](
irs::iql::proxy_filter& node, const std::locale&, void* const&, const irs::iql::function_arg::fn_args_t& args
)->bool {
return args.empty() && SUCCESS == init(node.proxy<iresearch::Or>(), find_node(node_id));
}
);
return SUCCESS;
case query_node::NodeType::INTERSECTION:
buf.emplace_back(std::move(fnArgs), [this, node_id](
irs::iql::proxy_filter& node, const std::locale&, void* const&, const irs::iql::function_arg::fn_args_t& args
)->bool {
return args.empty() && SUCCESS == init(node.proxy<iresearch::And>(), find_node(node_id));
});
return SUCCESS;
case query_node::NodeType::BOOL_TRUE:
buf.emplace_back(std::move(fnArgs), [this, node_id](
irs::iql::proxy_filter& node, const std::locale&, void* const&, const irs::iql::function_arg::fn_args_t& args
)->bool {
auto& argNode = find_node(node_id);
return args.empty() && SUCCESS == init(
argNode.bNegated
? node.proxy<iresearch::Not>().filter<iresearch::all>()
: node.proxy<iresearch::all>(),
argNode
);
});
return SUCCESS;
case query_node::NodeType::FUNCTION:
fnArgs.reserve(node.children.size());
// build function argument list
for (size_t i = 0, count = node.children.size(); i < count; ++i) {
auto& argNodeId = node.children[i];
auto errorNodeId = append_function_arg(fnArgs, argNodeId);
if (SUCCESS != errorNodeId) {
return UNKNOWN == errorNodeId ? argNodeId : errorNodeId;
}
}
if (node.pFnBoolean) {
if (node.bNegated) {
auto fnBranchArg = [this, node_id](
irs::iql::proxy_filter& node,
const std::locale& locale,
void* const& cookie,
const std::vector<irs::iql::function_arg>& args
)->bool {
auto& argNode = find_node(node_id);
return argNode.pFnBoolean && function(*(argNode.pFnBoolean))(
node.proxy<iresearch::Not>().filter<irs::iql::proxy_filter>(), locale, cookie, args
);
};
if (node.pFnSequence) {
buf.emplace_back(std::move(fnArgs), function(*(node.pFnSequence)), std::move(fnBranchArg));
}
else {
buf.emplace_back(std::move(fnArgs), std::move(fnBranchArg));
}
return SUCCESS;
}
if (node.pFnSequence) {
buf.emplace_back(std::move(fnArgs), function(*(node.pFnSequence)), function(*(node.pFnBoolean)));
}
else {
buf.emplace_back(std::move(fnArgs), function(*(node.pFnBoolean)));
}
return SUCCESS;
}
if (node.pFnSequence) {
buf.emplace_back(std::move(fnArgs), function(*(node.pFnSequence)));
return SUCCESS;
}
return node_id; // not a supported function type
case query_node::NodeType::EQUAL: // fall through
case query_node::NodeType::LIKE:
buf.emplace_back(std::move(fnArgs), [this, node_id](
irs::iql::proxy_filter& node, const std::locale&, void* const&, const irs::iql::function_arg::fn_args_t& args
)->bool {
auto& argNode = find_node(node_id);
return args.empty() && SUCCESS == init(
argNode.bNegated
? node.proxy<iresearch::Not>().filter<irs::iql::proxy_filter>()
: node,
argNode
);
});
return SUCCESS;
case query_node::NodeType::SEQUENCE:
buf.emplace_back(iresearch::ref_cast<iresearch::byte_type>(iresearch::string_ref(node.sValue)));
return SUCCESS;
default: {} // NOOP
}
return node_id; // unknown arg type
}
// initialize node children
irs::iql::parser::semantic_type parse_context::children(
iresearch::boolean_filter& node, const std::vector<size_t>& children
) const {
for (auto& childId: children) {
auto errorNodeId = childId;
auto& child = find_node(childId);
switch (child.type) {
case query_node::NodeType::UNION:
errorNodeId = init(node.add<iresearch::Or>(), child);
break;
case query_node::NodeType::INTERSECTION:
errorNodeId = init(node.add<iresearch::And>(), child);
break;
case query_node::NodeType::FUNCTION:
if (!child.pFnBoolean) {
break; // only boolean functions supported as branches
}
// fall through
case query_node::NodeType::EQUAL:
case query_node::NodeType::LIKE:
errorNodeId = child.bNegated
? init(node.add<iresearch::Not>(), child)
: init(node.add<irs::iql::proxy_filter>(), child);
break;
default: {} // NOOP
}
// on error terminate traversal
if (SUCCESS != errorNodeId) {
return UNKNOWN == errorNodeId ? childId : errorNodeId;
}
}
return SUCCESS;
}
irs::iql::parser::semantic_type parse_context::eval(
iresearch::bstring& buf,
const query_node& src
) const {
switch(src.type) {
case query_node::NodeType::SEQUENCE:
buf.append(iresearch::ref_cast<iresearch::byte_type>(iresearch::string_ref(src.sValue)));
break;
case query_node::NodeType::FUNCTION:
if (src.pFnSequence) {
auto errorNodeId = eval<irs::iql::sequence_function, std::locale, void*>(
buf, function(*(src.pFnSequence)), src.children, m_locale, m_cookie
);
// on error terminate traversal
if (SUCCESS != errorNodeId) {
return errorNodeId;
}
break;
}
default:
return UNKNOWN;
}
return SUCCESS;
}
irs::iql::parser::semantic_type parse_context::order(
iresearch::order& node, const std::vector<std::pair<size_t, bool>>& order
) const {
for (auto orderTerm: order) {
auto& ascending = orderTerm.second;
auto& childId = orderTerm.first;
auto& childNode = find_node(childId);
switch(childNode.type) {
case query_node::NodeType::FUNCTION: {
if (!childNode.pFnOrder) {
return childId; // no order function
}
auto errorNodeId = eval<irs::iql::order_function, std::locale, void*, bool>(
node,
function(*(childNode.pFnOrder)),
childNode.children,
m_locale,
m_cookie,
ascending
);
// on error terminate traversal
if (SUCCESS != errorNodeId) {
return UNKNOWN == errorNodeId ? childId : errorNodeId;
}
break;
}
case query_node::NodeType::SEQUENCE:
// field-term order is not yet supported by iresearch::order
default:
return childId;
}
}
return SUCCESS;
}
irs::iql::parser::semantic_type parse_context::initRange(
irs::iql::proxy_filter& node,
const iresearch::string_ref& field,
const irs::iql::parser::semantic_type& min_node_id, bool min_inclusive,
const irs::iql::parser::semantic_type& max_value_id, bool max_inclusive
) const {
auto& min_node = find_node(min_node_id);
auto& max_node = find_node(max_value_id);
irs::iql::function_arg::fn_args_t args;
args.reserve(2);
if (query_node::NodeType::UNKNOWN == min_node.type) {
args.emplace_back(iresearch::bytes_ref::NIL);
}
else {
auto errorNodeId = append_function_arg(args, min_node_id);
if (SUCCESS != errorNodeId) {
return UNKNOWN == errorNodeId ? min_node_id : errorNodeId;
}
}
if (query_node::NodeType::UNKNOWN == max_node.type) {
args.emplace_back(iresearch::bytes_ref::NIL);
}
else {
auto errorNodeId = append_function_arg(args, max_value_id);
if (SUCCESS != errorNodeId) {
return UNKNOWN == errorNodeId ? min_node_id : errorNodeId;
}
}
// .............................................................................
// build a branch off of 'node' for IQL field/value
// .............................................................................
if (min_inclusive) {
if (max_inclusive) {
return m_branch_builders.range_ii(node, m_locale, field, m_cookie, args)
? SUCCESS : UNKNOWN;
}
return m_branch_builders.range_ie(node, m_locale, field, m_cookie, args)
? SUCCESS : UNKNOWN;
}
if (max_inclusive) {
return m_branch_builders.range_ei(node, m_locale, field, m_cookie, args)
? SUCCESS : UNKNOWN;
}
return m_branch_builders.range_ee(node, m_locale, field, m_cookie, args)
? SUCCESS : UNKNOWN;
}
irs::iql::parser::semantic_type parse_context::initSimilar(
irs::iql::proxy_filter& node,
const iresearch::string_ref& field,
const irs::iql::parser::semantic_type& value_id
) const {
irs::iql::function_arg::fn_args_t args;
args.reserve(1);
auto errorNodeId = append_function_arg(args, value_id);
if (SUCCESS != errorNodeId) {
return UNKNOWN == errorNodeId ? value_id : errorNodeId;
}
// build a branch off of 'node' for IQL field/value
return m_branch_builders.similar(node, m_locale, field, m_cookie, args)
? SUCCESS : UNKNOWN;
}
NS_END
NS_ROOT
NS_BEGIN(iql)
// -----------------------------------------------------------------------------
// --SECTION-- constructors and destructors
// -----------------------------------------------------------------------------
query_builder::branch_builders::branch_builders(
const branch_builder_function_t* v_range_ee /*= nullptr*/,
const branch_builder_function_t* v_range_ei /*= nullptr*/,
const branch_builder_function_t* v_range_ie /*= nullptr*/,
const branch_builder_function_t* v_range_ii /*= nullptr*/,
const branch_builder_function_t* v_similar /*= nullptr*/
): range_ee(v_range_ee ? *v_range_ee : RANGE_EE_BRANCH_BUILDER),
range_ei(v_range_ei ? *v_range_ei : RANGE_EI_BRANCH_BUILDER),
range_ie(v_range_ie ? *v_range_ie : RANGE_IE_BRANCH_BUILDER),
range_ii(v_range_ii ? *v_range_ii : RANGE_II_BRANCH_BUILDER),
similar(v_similar ? *v_similar : SIMILAR_BRANCH_BUILDER) {
}
query_builder::query_builder(
const functions& iql_functions /*= functions::DEFAULT()*/,
const branch_builders& branch_builders /*= branch_builders::DEFAULT()*/
): branch_builders_(branch_builders), iql_functions_(iql_functions) {
}
// -----------------------------------------------------------------------------
// --SECTION-- public functions
// -----------------------------------------------------------------------------
/*static*/ const query_builder::branch_builders& query_builder::branch_builders::DEFAULT() {
static const branch_builders BRANCH_BUILDERS(
nullptr, nullptr, nullptr, nullptr, nullptr
);
return BRANCH_BUILDERS;
}
query query_builder::build(
const std::string& query,
const std::locale& locale,
void* cookie /*= nullptr*/,
proxy_filter* root /*= nullptr*/
) const {
parse_context ctx(query, locale, cookie, iql_functions_, branch_builders_);
parser parser(ctx);
if (parser.parse()) {
return ctx.buildError();
}
if (!root) {
return ctx.build();
}
auto result = ctx.build();
// link the query into both the root and result.filter via two LinkNodes
if (!result.error) {
auto& link = root->proxy<LinkNode>(result.filter.get());
result.filter.release();
result.filter = LinkNode::make(link);
}
return result;
}
// -----------------------------------------------------------------------------
// --SECTION-- private functions
// -----------------------------------------------------------------------------
DEFINE_FILTER_TYPE(proxy_filter);
DEFINE_FACTORY_DEFAULT(proxy_filter);
NS_END // iql
NS_END // NS_ROOT
#if defined (__GNUC__)
#pragma GCC diagnostic pop
#endif
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------