protected virtual BaseQuery HandleNot(NotNode node, ElasticSearchQueryMapperState state)
        {
            var query = Handle(node.Operand, state);

            //TODO: this works for basic term queries. While the LuceneProvider is somewhat the same, the SolrProvider had extra logic presumably to handle specific cases.
            return(!query);
        }
        public override ElasticSearchQuery MapQuery(IndexQuery query)
        {
            var state       = new ElasticSearchQueryMapperState();
            var mappedQuery = Handle(query.RootNode, state);

            return(new ElasticSearchQuery(mappedQuery, state.FilterQuery, state.AdditionalQueryMethods, state.VirtualFieldProcessors, state.FacetQueries));
        }
        protected virtual BaseQuery HandleOr(OrNode node, ElasticSearchQueryMapperState state)
        {
            //TODO: the code below specifically handles IsNullOrEmpty method, seems like a lot to handle one case... is there a better place for it?
            //Lucene provider doesn't have this code, but Solr provider does
            if (node.LeftNode.NodeType == QueryNodeType.Equal && node.RightNode.NodeType == QueryNodeType.Equal)
            {
                var leftNode  = (EqualNode)node.LeftNode;
                var rightNode = (EqualNode)node.RightNode;
                if (leftNode.RightNode.NodeType == QueryNodeType.Constant && rightNode.RightNode.NodeType == QueryNodeType.Constant)
                {
                    var leftNodeValue  = ((ConstantNode)leftNode.RightNode).Value;
                    var rightNodeValue = ((ConstantNode)rightNode.RightNode).Value;
                    if ((string)leftNodeValue == string.Empty && rightNodeValue == null)
                    {
                        var fieldName = ((FieldNode)leftNode.LeftNode).FieldKey;
                        //TODO: this query works for 99% of items, however if a field contains a stopword and only a stopword, then it's treated as "missing" by ES
                        //For example, say you have an item whose "Title" field contains just the word "To" (which is a stopword), if you try to search for all items
                        //without a value in the "Title" field (i.e. the field is missing), ES will still return the item whose "Title" field contains just the word "To".
                        //There's likely a better query to use... or maybe not, maybe it can only be done with analyzers. who could say?
                        return(Query.Filtered(fq => fq.Filter(f => f.Missing(fieldName))));
                    }
                }
            }

            var query1 = Handle(node.LeftNode, state);
            var query2 = Handle(node.RightNode, state);

            if (query1)
            {
                return(query1);
            }

            return(query1 | query2);
        }
        protected virtual BaseQuery HandleBetween(BetweenNode node, ElasticSearchQueryMapperState state)
        {
            var excludeLowerBound = !(node.Inclusion == Inclusion.Both || node.Inclusion == Inclusion.Lower);
            var excludeUpperBound = !(node.Inclusion == Inclusion.Both || node.Inclusion == Inclusion.Upper);
            var fieldName         = GetFormattedFieldName(node.Field);
            var lowerBound        = ValueFormatter.FormatValueForIndexStorage(node.From);
            var upperBound        = ValueFormatter.FormatValueForIndexStorage(node.To);

            return(Query.Range(delegate(RangeQueryDescriptor <dynamic> descriptor)
            {
                descriptor.From(lowerBound.ToString());
                if (excludeLowerBound)
                {
                    descriptor.FromExclusive();
                }

                descriptor.To(upperBound.ToString());
                if (excludeUpperBound)
                {
                    descriptor.ToExclusive();
                }

                descriptor.OnField(fieldName);
            }));
        }
        protected virtual BaseQuery HandleStartsWith(StartsWithNode node, ElasticSearchQueryMapperState state)
        {
            var fieldName = GetFormattedFieldName(node);
            var valueNode = QueryHelper.GetValueNode <string>(node);

            var queryValue = ValueFormatter.FormatValueForIndexStorage(valueNode.Value);

            return(Query.Prefix(fieldName, queryValue.ToString()));
        }
        protected BaseQuery HandleWildcardMatch(WildcardMatchNode node, ElasticSearchQueryMapperState mappingState)
        {
            var fieldName = GetFormattedFieldName(node);
            var valueNode = QueryHelper.GetValueNode <string>(node);

            var queryValue = ValueFormatter.FormatValueForIndexStorage(valueNode.Value);

            //TODO: same as the HandleContains method... is there a better way to do this without wildcard queries?
            return(Query.Wildcard(fieldName, queryValue.ToString()));
        }
        protected virtual BaseQuery HandleEndsWith(EndsWithNode node, ElasticSearchQueryMapperState state)
        {
            var fieldName = GetFormattedFieldName(node);
            var valueNode = QueryHelper.GetValueNode <string>(node);

            var queryValue = ValueFormatter.FormatValueForIndexStorage(valueNode.Value);

            //TODO: same as the HandleContains method... is there a better way to do this without wildcard queries?
            return(Query.Wildcard(fieldName, "*" + queryValue));
        }
        protected virtual BaseQuery HandleField(FieldNode node, ElasticSearchQueryMapperState state)
        {
            if (node.FieldType != typeof(bool))
            {
                throw new NotSupportedException(string.Format("The query node type '{0}' is not supported in this context.", node.NodeType));
            }

            var fieldName = GetFormattedFieldName(node.FieldKey);

            return(Query.Term(fieldName, true.ToString()));
        }
        protected virtual BaseQuery HandleAnd(AndNode node, ElasticSearchQueryMapperState state)
        {
            var query1 = Handle(node.LeftNode, state);
            var query2 = Handle(node.RightNode, state);

            if (!query1)
            {
                return(query1);
            }
            return(query1 & query2);
        }
        protected virtual BaseQuery HandleLessThanOrEqual(LessThanOrEqualNode node, ElasticSearchQueryMapperState state)
        {
            BaseQuery query;

            var fieldNode = node.GetFieldNode();
            var valueNode = QueryHelper.GetValueNode(node, fieldNode.FieldType);

            if (ProcessAsVirtualField(fieldNode, valueNode, node.Boost, ComparisonType.LessThanOrEqual, state, out query))
            {
                return(query);
            }

            return(HandleLessThanOrEqual(fieldNode.FieldKey, valueNode.Value, node.Boost));
        }
        protected BaseQuery HandleLike(LikeNode node, ElasticSearchQueryMapperState mappingState)
        {
            var fieldName      = GetFormattedFieldName(node);
            var valueNode      = QueryHelper.GetValueNode <string>(node);
            var formattedValue = ValueFormatter.FormatValueForIndexStorage(valueNode.Value);

            var query =
                Query.Fuzzy(
                    descriptor =>
                    descriptor.OnField(fieldName)
                    .MinSimilarity(node.MinimumSimilarity)
                    .Like(formattedValue.ToStringOrEmpty())
                    .Boost(node.Boost));

            return(query);
        }
        protected virtual BaseQuery HandleContains(ContainsNode node, ElasticSearchQueryMapperState state)
        {
            var fieldName = GetFormattedFieldName(node);
            var valueNode = QueryHelper.GetValueNode <string>(node);

            //wildcard query values should be lowercase, even if the value is stored case-sensitively
            //not sure why it needs to be this way - and there's probably some way to make it work either way using analyzers...
            var queryValue = ValueFormatter.FormatValueForIndexStorage(valueNode.Value).ToString().ToLowerInvariant();

            //TODO: evidently, wildcard queries do not scale well for large indexes
            //therefore, this query should be replaced with something else.
            //rumor has it the use of n-gram analyzers and regular term queries works better than wildcard queries.
            //question is, how to do this in generic terms so that it works "out of the box"?
            //probably an easy way, just not today...
            return(Query.Wildcard(fieldName, "*" + queryValue + "*"));
        }
        /// <summary>
        /// Regex query not currently supported in NEST, and there's no straightforward way to append raw query text to any parsed queries.
        /// </summary>
        /// <param name="node"></param>
        /// <param name="state"></param>
        /// <returns></returns>
        protected virtual BaseQuery HandleMatches(MatchesNode node, ElasticSearchQueryMapperState state)
        {
            //var fieldName = GetFormattedFieldName(node);
            //var valueNode = QueryHelper.GetValueNode<string>(node);
            //var formattedValue = ValueFormatter.FormatValueForIndexStorage(valueNode.Value);

            //using (var sw = new StringWriter())
            //{
            //	using (var jw = new JsonTextWriter(sw))
            //	{
            //		jw.WriteStartObject();
            //			jw.WritePropertyName("regexp");
            //			jw.WriteStartObject();
            //				jw.WritePropertyName(fieldName);
            //				jw.WriteStartObject();
            //					jw.WritePropertyName("value");
            //					jw.WriteValue(formattedValue);
            //					if (Math.Abs(node.Boost - 1f) > float.Epsilon)
            //					{
            //						jw.WritePropertyName("boost");
            //						jw.WriteValue(node.Boost);
            //					}
            //				jw.WriteEndObject();
            //				if (node.RegexOptions != null && !string.IsNullOrEmpty(node.RegexOptions.ToString()))
            //				{
            //					jw.WritePropertyName("flags");
            //					jw.WriteValue(node.RegexOptions);
            //				}
            //			jw.WriteEndObject();
            //		jw.WriteEndObject();
            //	}

            //	var rawQuery = sw.ToString();
            //	return rawQuery;
            //}

            //rawQuery = "{\"regexp\" : { \"" + fieldName + "\" : { \"value\" : \"" + formattedValue + "\", \"flags\" : \"" + node.RegexOptions + "\" } }";

            throw new NotImplementedException("Matches expression is not implemented (i.e. no regex queries)");
        }
        protected virtual BaseQuery HandleWhere(WhereNode node, ElasticSearchQueryMapperState state)
        {
            var query1 = Handle(node.PredicateNode, state);
            var query2 = Handle(node.SourceNode, state);

            if (query1 == Query.MatchAll() && query2 == Query.MatchAll())
            {
                return(query1);
            }
            if (query1 == Query.MatchAll() || query2 == Query.MatchAll())
            {
                if (query1 != Query.MatchAll())
                {
                    return(query1);
                }
                if (query2 != Query.MatchAll())
                {
                    return(query2);
                }
            }

            return(query1 & query2);
        }
 protected virtual void StripFacetPivotOn(FacetPivotOnNode node, ElasticSearchQueryMapperState state)
 {
     state.FacetQueries.Add(new FacetQuery(null, node.Fields, node.MinimumNumberOfDocuments, node.FilterValues));
 }
 protected virtual BaseQuery HandleMatchNone(MatchNoneNode node, ElasticSearchQueryMapperState state)
 {
     //TODO: does this actually work? not sure about the syntax...
     return(!Query.MatchAll());
     //return Query.Bool(descriptor => descriptor.MustNot(new[] { Query.MatchAll() }));
 }
 protected virtual BaseQuery HandleMatchAll(MatchAllNode node, ElasticSearchQueryMapperState state)
 {
     return(Query.MatchAll());
 }
 protected virtual BaseQuery HandleFilter(FilterNode node, ElasticSearchQueryMapperState state)
 {
     return(Handle(node.PredicateNode, state));
 }
        protected virtual BaseQuery Handle(QueryNode node, ElasticSearchQueryMapperState state)
        {
            switch (node.NodeType)
            {
            case QueryNodeType.All:
                StripAll((AllNode)node, state.AdditionalQueryMethods);
                return(Handle(((AllNode)node).SourceNode, state));

            case QueryNodeType.And:
                return(HandleAnd((AndNode)node, state));

            case QueryNodeType.Any:
                StripAny((AnyNode)node, state.AdditionalQueryMethods);
                return(Handle(((AnyNode)node).SourceNode, state));

            case QueryNodeType.Between:
                return(HandleBetween((BetweenNode)node, state));

            case QueryNodeType.Cast:
                StripCast((CastNode)node, state.AdditionalQueryMethods);
                return(Handle(((CastNode)node).SourceNode, state));

            case QueryNodeType.Contains:
                return(HandleContains((ContainsNode)node, state));

            case QueryNodeType.Count:
                StripCount((CountNode)node, state.AdditionalQueryMethods);
                return(Handle(((CountNode)node).SourceNode, state));

            case QueryNodeType.ElementAt:
                StripElementAt((ElementAtNode)node, state.AdditionalQueryMethods);
                return(Handle(((ElementAtNode)node).SourceNode, state));

            case QueryNodeType.EndsWith:
                return(HandleEndsWith((EndsWithNode)node, state));

            case QueryNodeType.Equal:
                return(HandleEqual((EqualNode)node, state));

            case QueryNodeType.Field:
                return(HandleField((FieldNode)node, state));

            case QueryNodeType.First:
                StripFirst((FirstNode)node, state.AdditionalQueryMethods);
                return(Handle(((FirstNode)node).SourceNode, state));

            case QueryNodeType.GreaterThan:
                return(HandleGreaterThan((GreaterThanNode)node, state));

            case QueryNodeType.GreaterThanOrEqual:
                return(HandleGreaterThanOrEqual((GreaterThanOrEqualNode)node, state));

            case QueryNodeType.Last:
                StripLast((LastNode)node, state.AdditionalQueryMethods);
                return(Handle(((LastNode)node).SourceNode, state));

            case QueryNodeType.LessThan:
                return(HandleLessThan((LessThanNode)node, state));

            case QueryNodeType.LessThanOrEqual:
                return(HandleLessThanOrEqual((LessThanOrEqualNode)node, state));

            case QueryNodeType.MatchAll:
                return(HandleMatchAll((MatchAllNode)node, state));

            case QueryNodeType.MatchNone:
                return(HandleMatchNone((MatchNoneNode)node, state));

            case QueryNodeType.Max:
                StripMax((MaxNode)node, state.AdditionalQueryMethods);
                return(Handle(((MaxNode)node).SourceNode, state));

            case QueryNodeType.Min:
                StripMin((MinNode)node, state.AdditionalQueryMethods);
                return(Handle(((MinNode)node).SourceNode, state));

            case QueryNodeType.Not:
                return(HandleNot((NotNode)node, state));

            case QueryNodeType.Or:
                return(HandleOr((OrNode)node, state));

            case QueryNodeType.OrderBy:
                StripOrderBy((OrderByNode)node, state.AdditionalQueryMethods);
                return(Handle(((OrderByNode)node).SourceNode, state));

            case QueryNodeType.Select:
                StripSelect((SelectNode)node, state.AdditionalQueryMethods);
                return(Handle(((SelectNode)node).SourceNode, state));

            case QueryNodeType.Single:
                StripSingle((SingleNode)node, state.AdditionalQueryMethods);
                return(Handle(((SingleNode)node).SourceNode, state));

            case QueryNodeType.Skip:
                StripSkip((SkipNode)node, state.AdditionalQueryMethods);
                return(Handle(((SkipNode)node).SourceNode, state));

            case QueryNodeType.StartsWith:
                return(HandleStartsWith((StartsWithNode)node, state));

            case QueryNodeType.Take:
                StripTake((TakeNode)node, state.AdditionalQueryMethods);
                return(Handle(((TakeNode)node).SourceNode, state));

            case QueryNodeType.Where:
                return(HandleWhere((WhereNode)node, state));

            case QueryNodeType.Matches:
                return(HandleMatches((MatchesNode)node, state));

            case QueryNodeType.Filter:
                if (state.FilterQuery != null)
                {
                    var filterQuery = state.FilterQuery;
                    state.FilterQuery = filterQuery & HandleFilter((FilterNode)node, state);
                    break;
                }
                state.FilterQuery = HandleFilter((FilterNode)node, state);
                break;

            case QueryNodeType.GetResults:
                StripGetResults((GetResultsNode)node, state.AdditionalQueryMethods);
                return(Handle(((GetResultsNode)node).SourceNode, state));

            case QueryNodeType.GetFacets:
                StripGetFacets((GetFacetsNode)node, state.AdditionalQueryMethods);
                return(Handle(((GetFacetsNode)node).SourceNode, state));

            case QueryNodeType.FacetOn:
                StripFacetOn((FacetOnNode)node, state);
                return(Handle(((FacetOnNode)node).SourceNode, state));

            case QueryNodeType.FacetPivotOn:
                StripFacetPivotOn((FacetPivotOnNode)node, state);
                return(Handle(((FacetPivotOnNode)node).SourceNode, state));

            case QueryNodeType.WildcardMatch:
                return(HandleWildcardMatch((WildcardMatchNode)node, state));

            case QueryNodeType.Like:
                return(HandleLike((LikeNode)node, state));

            default:
                throw new NotSupportedException(string.Format("Unknown query node type: '{0}'", node.NodeType));
            }

            return(Handle(((FilterNode)node).SourceNode, state));
        }
        protected virtual bool ProcessAsVirtualField(FieldNode fieldNode, ConstantNode valueNode, float boost, ComparisonType comparison, ElasticSearchQueryMapperState state, out BaseQuery query)
        {
            query = null;
            if (_fieldQueryTranslators == null)
            {
                return(false);
            }
            var translator = _fieldQueryTranslators.GetTranslator(fieldNode.FieldKey.ToLowerInvariant());

            if (translator == null)
            {
                return(false);
            }

            var formattedValue = ValueFormatter.FormatValueForIndexStorage(valueNode.Value);
            var fieldQuery     = translator.TranslateFieldQuery(fieldNode.FieldKey, formattedValue, comparison, FieldNameTranslator);         //TODO: does the fieldNode.FieldKey value need to be formatted here?

            if (fieldQuery == null)
            {
                return(false);
            }

            var queryList = new List <BaseQuery>();

            if (fieldQuery.FieldComparisons != null)
            {
                foreach (var tuple in fieldQuery.FieldComparisons)
                {
                    var indexFieldName = FieldNameTranslator.GetIndexFieldName(tuple.Item1);
                    switch (tuple.Item3)
                    {
                    case ComparisonType.Equal:
                        queryList.Add(HandleEqual(indexFieldName, tuple.Item2, boost));
                        break;

                    case ComparisonType.LessThan:
                        queryList.Add(HandleLessThan(indexFieldName, tuple.Item2, boost));
                        break;

                    case ComparisonType.LessThanOrEqual:
                        queryList.Add(HandleLessThanOrEqual(indexFieldName, tuple.Item2, boost));
                        break;

                    case ComparisonType.GreaterThan:
                        queryList.Add(HandleGreaterThan(indexFieldName, tuple.Item2, boost));
                        break;

                    case ComparisonType.GreaterThanOrEqual:
                        queryList.Add(HandleGreaterThanOrEqual(indexFieldName, tuple.Item2, boost));
                        break;

                    default:
                        throw new InvalidOperationException("Unknown comparison type: " + tuple.Item3);
                    }
                }
                foreach (var q in queryList)
                {
                    if (query == null)
                    {
                        query = q;
                    }
                    else
                    {
                        query &= q;
                    }
                }
            }

            if (fieldQuery.QueryMethods != null)
            {
                foreach (var method in fieldQuery.QueryMethods)
                {
                    state.AdditionalQueryMethods.Add(method);
                }
            }

            state.VirtualFieldProcessors.Add(translator);

            return(true);
        }