Example #1
0
        private static void AddTopColumnValues(IntelliSenseResult result, TermExpression lastTerm, IntelliSenseGuidance guidance, List <IntelliSenseItem> suggestions, Table singleTable, ColumnDetails singleColumn)
        {
            string completeQuery = GetCompleteQueryPrefix(result);

            // Recommend the top ten values in the column with the prefix typed so far
            DistinctResult topValues = singleTable.Query(new DistinctQueryTop(singleColumn.Name, lastTerm.Value.ToString(), completeQuery, 10));
            int            total     = (int)topValues.Total;

            if (topValues.Total == 0)
            {
                return;
            }

            // Walk values in order for ==, :, ::, backwards with inverse percentages for !=
            bool isNotEquals = lastTerm.Operator == Operator.NotEquals;
            int  start       = isNotEquals ? topValues.Values.RowCount - 1 : 0;
            int  end         = isNotEquals ? -1 : topValues.Values.RowCount;
            int  step        = isNotEquals ? -1 : 1;

            for (int i = start; i != end; i += step)
            {
                string value         = topValues.Values[i, 0].ToString();
                int    countForValue = (int)topValues.Values[i, 1];
                if (isNotEquals)
                {
                    countForValue = (int)topValues.Total - countForValue;
                }

                if ((countForValue > 1 || total <= 10) && value.StartsWith(guidance.Value, StringComparison.OrdinalIgnoreCase) && value.Length < 100)
                {
                    suggestions.Add(new IntelliSenseItem(QueryTokenCategory.Value, QueryParser.WrapValue(topValues.Values[i, 0]), countForValue, topValues.Total));
                }
            }
        }
Example #2
0
        private static void AddValueDistribution(IntelliSenseResult result, TermExpression lastTerm, IntelliSenseGuidance guidance, List <IntelliSenseItem> suggestions, Table singleTable, ColumnDetails singleColumn)
        {
            string completeQuery = GetCompleteQueryPrefix(result);

            bool inclusive = (lastTerm.Operator == Operator.LessThanOrEqual || lastTerm.Operator == Operator.GreaterThan);
            bool reverse   = (lastTerm.Operator == Operator.GreaterThan || lastTerm.Operator == Operator.GreaterThanOrEqual);

            // Recommend the top ten values in the column with the prefix typed so far
            DataBlockResult distribution = singleTable.Query(new DistributionQuery(singleColumn.Name, completeQuery, inclusive));

            if (distribution.Total == 0 || distribution.Details.Succeeded == false)
            {
                return;
            }

            int countSoFar;

            if (reverse)
            {
                countSoFar = (int)distribution.Values[distribution.Values.RowCount - 1, 1];

                for (int i = distribution.Values.RowCount - 2; i >= 0; --i)
                {
                    string value         = distribution.Values[i, 0].ToString();
                    double frequency     = (double)countSoFar / (double)(distribution.Total);
                    int    countForRange = (int)distribution.Values[i, 1];

                    if ((distribution.Values.RowCount == 2 || (int)distribution.Values[i + 1, 1] > 0) && value.StartsWith(guidance.Value, StringComparison.OrdinalIgnoreCase))
                    {
                        suggestions.Add(new IntelliSenseItem(QueryTokenCategory.Value, QueryParser.WrapValue(distribution.Values[i, 0]), countSoFar, distribution.Total));
                    }

                    countSoFar += countForRange;
                }
            }
            else
            {
                countSoFar = 0;

                for (int i = 0; i < distribution.Values.RowCount - 1; ++i)
                {
                    string value         = distribution.Values[i, 0].ToString();
                    int    countForRange = (int)distribution.Values[i, 1];
                    countSoFar += countForRange;

                    double frequency = (double)countSoFar / (double)(distribution.Total);

                    if ((distribution.Values.RowCount == 2 || countForRange > 0) && value.StartsWith(guidance.Value, StringComparison.OrdinalIgnoreCase))
                    {
                        suggestions.Add(new IntelliSenseItem(QueryTokenCategory.Value, QueryParser.WrapValue(distribution.Values[i, 0]), countSoFar, distribution.Total));
                    }
                }
            }
        }
Example #3
0
        /// <summary>
        ///  CompleteQuery takes a query before the cursor, the IntelliSenseResult, a selected IntelliSenseItem, and the
        ///  completion character and returns the proper completed query.
        ///
        ///  It removes the token in progress, adds the 'CompleteAs' value, a space, and the non-whitespace completion character.
        /// </summary>
        /// <param name="queryBeforeCursor">Query up to the cursor position</param>
        /// <param name="result">IntelliSenseResult from GetIntelliSenseItems</param>
        /// <param name="selectedItem">IntelliSenseItem selected</param>
        /// <param name="completionCharacter">Completion Character typed</param>
        /// <returns>New Arriba Query after completion</returns>
        public string CompleteQuery(string queryBeforeCursor, IntelliSenseResult result, IntelliSenseItem selectedItem, char completionCharacter)
        {
            // If there is no completion for this item (grammar suggestions), just append the character
            if (selectedItem == null || String.IsNullOrEmpty(selectedItem.CompleteAs))
            {
                return(queryBeforeCursor + completionCharacter);
            }

            // Add the value to complete. Add a space afterward for non-values (so we don't jump to the next term). Add a space if you completed with space.
            string separator = (selectedItem.Category == QueryTokenCategory.Value && completionCharacter != ' ' ? "" : " ");
            string newQuery  = result.Complete + selectedItem.CompleteAs + separator;

            // If the completion character isn't '\t' or ' ', add the completion character as well
            if (completionCharacter != '\n' && completionCharacter != '\t' && completionCharacter != ' ')
            {
                newQuery += completionCharacter;
            }

            return(newQuery);
        }
Example #4
0
        private static void AddTopColumnValues(IntelliSenseResult result, TermExpression lastTerm, IntelliSenseGuidance guidance, List <IntelliSenseItem> suggestions, Table singleTable, ColumnDetails singleColumn)
        {
            // Lame, to turn single terms into AllQuery [normally they return nothing]
            string completeQuery = QueryParser.Parse(result.Complete).ToString();

            // Recommend the top ten values in the column with the prefix typed so far
            DistinctResult topValues = singleTable.Query(new DistinctQueryTop(singleColumn.Name, completeQuery, 10));
            int            total     = (int)topValues.Total;

            if (topValues.Total == 0)
            {
                return;
            }

            // Walk values in order for ==, :, ::, backwards with inverse percentages for !=
            bool isNotEquals = lastTerm.Operator == Operator.NotEquals;
            int  start       = isNotEquals ? topValues.Values.RowCount - 1 : 0;
            int  end         = isNotEquals ? -1 : topValues.Values.RowCount;
            int  step        = isNotEquals ? -1 : 1;

            for (int i = start; i != end; i += step)
            {
                string value         = topValues.Values[i, 0].ToString();
                int    countForValue = (int)topValues.Values[i, 1];
                if (isNotEquals)
                {
                    countForValue = (int)topValues.Total - countForValue;
                }
                double frequency = (double)countForValue / (double)(topValues.Total);

                if ((countForValue > 1 || total <= 10) && value.StartsWith(guidance.Value, StringComparison.OrdinalIgnoreCase))
                {
                    string hint = (countForValue == topValues.Total ? "all" : frequency.ToString("P0"));
                    suggestions.Add(new IntelliSenseItem(QueryTokenCategory.Value, QueryScanner.WrapValue(value), hint));
                }
            }
        }
Example #5
0
 private static string GetCompleteQueryPrefix(IntelliSenseResult result)
 {
     // Lame, to turn single terms into AllQuery [normally they return nothing]
     return(QueryParser.Parse(result.Complete).ToString());
 }
Example #6
0
        private static void AddSuggestionsForTerm(IReadOnlyCollection <Table> targetTables, IntelliSenseResult result, TermExpression lastTerm, IntelliSenseGuidance guidance, List <IntelliSenseItem> suggestions)
        {
            if (lastTerm != null && lastTerm.ColumnName == "*")
            {
                object termValue = lastTerm.Value;
                List <Tuple <string, int, int> > columnsForTerm = new List <Tuple <string, int, int> >();

                foreach (Table table in targetTables)
                {
                    DataBlockResult columns = table.Query(new TermInColumnsQuery(termValue.ToString(), GetCompleteQueryPrefix(result)));
                    for (int i = 0; i < columns.Values.RowCount; ++i)
                    {
                        columnsForTerm.Add(new Tuple <string, int, int>((string)columns.Values[i, 0], (int)columns.Values[i, 1], (int)columns.Total));
                    }
                }

                // Sort overall set by frequency descending
                columnsForTerm.Sort((left, right) => ((double)right.Item2 / (double)right.Item3).CompareTo((double)left.Item2 / (double)left.Item3));

                // Add top 10 suggestions
                int countToReturn = Math.Min(10, columnsForTerm.Count);
                for (int i = 0; i < countToReturn; ++i)
                {
                    suggestions.Add(new IntelliSenseItem(QueryTokenCategory.ColumnName, QueryParser.WrapColumnName(columnsForTerm[i].Item1) + " : " + QueryParser.WrapValue(termValue), columnsForTerm[i].Item2, columnsForTerm[i].Item3));
                }
            }
        }
Example #7
0
        private static void AddSuggestionsForValue(IReadOnlyCollection <Table> targetTables, IntelliSenseResult result, TermExpression lastTerm, IntelliSenseGuidance guidance, ref bool spaceIsSafeCompletionCharacter, List <IntelliSenseItem> suggestions)
        {
            Table         singleTable;
            ColumnDetails singleColumn;

            if (!TryFindSingleMatchingColumn(targetTables, lastTerm, out singleTable, out singleColumn))
            {
                // If more than one table has the column, we can't recommend anything
                result.SyntaxHint = Value;
            }
            else
            {
                if (lastTerm.Operator == Operator.Equals || lastTerm.Operator == Operator.NotEquals || lastTerm.Operator == Operator.Matches || lastTerm.Operator == Operator.MatchesExact)
                {
                    AddTopColumnValues(result, lastTerm, guidance, suggestions, singleTable, singleColumn);
                }
                else if (lastTerm.Operator == Operator.LessThan || lastTerm.Operator == Operator.LessThanOrEqual || lastTerm.Operator == Operator.GreaterThan || lastTerm.Operator == Operator.GreaterThanOrEqual)
                {
                    AddValueDistribution(result, lastTerm, guidance, suggestions, singleTable, singleColumn);
                }

                Type columnType = singleTable.GetColumnType(singleColumn.Name);

                if (columnType == typeof(ByteBlock))
                {
                    result.SyntaxHint = StringValue;
                }
                else if (columnType == typeof(bool))
                {
                    if (suggestions.Count == 0)
                    {
                        AddWhenPrefixes(BooleanValues, guidance.Value, suggestions);
                    }
                    spaceIsSafeCompletionCharacter = true;
                }
                else if (columnType == typeof(DateTime))
                {
                    result.SyntaxHint = DateTimeValue;
                }
                else if (columnType == typeof(TimeSpan))
                {
                    result.SyntaxHint = TimeSpanValue;
                }
                else if (columnType == typeof(float) || columnType == typeof(double))
                {
                    result.SyntaxHint = FloatValue;
                }
                else if (columnType == typeof(byte) || columnType == typeof(sbyte) || columnType == typeof(short) || columnType == typeof(ushort) || columnType == typeof(int) || columnType == typeof(uint) || columnType == typeof(long) || columnType == typeof(ulong))
                {
                    result.SyntaxHint = IntegerValue;
                }
                else
                {
                    result.SyntaxHint = String.Format("<{0}>", columnType.Name);
                }
            }
        }
Example #8
0
        /// <summary>
        ///  Get the set of IntelliSense suggestions valid at the current position.
        ///  It's filtered to the set of valid query parts are the current position,
        ///  as well as the set of values the current partial value is a prefix for.
        /// </summary>
        /// <param name="queryBeforeCursor">Current Arriba Query up to the cursor position</param>
        /// <param name="targetTables">Table[s] which are valid for the current query</param>
        /// <returns>IntelliSenseResult reporting what to show</returns>
        public IntelliSenseResult GetIntelliSenseItems(string queryBeforeCursor, IReadOnlyCollection <Table> targetTables)
        {
            IntelliSenseResult result = new IntelliSenseResult()
            {
                Query = queryBeforeCursor, Incomplete = "", Complete = "", SyntaxHint = "", CompletionCharacters = new char[0], Suggestions = new List <IntelliSenseItem>()
            };

            // If no tables were passed, show no IntelliSense (hint that there's an error blocking all tables)
            if (queryBeforeCursor == null || targetTables == null || targetTables.Count == 0)
            {
                return(result);
            }

            // Filter the set of tables to those valid for the query so far
            targetTables = FilterToValidTablesForQuery(targetTables, queryBeforeCursor);

            // If no tables remain valid, show no IntelliSense (hint that there's an error blocking all tables)
            if (targetTables == null || targetTables.Count == 0)
            {
                return(result);
            }

            // Get grammatical categories valid after the query prefix
            TermExpression       lastTerm;
            IExpression          query;
            IntelliSenseGuidance guidance       = GetCurrentTokenOptions(queryBeforeCursor, out lastTerm, out query);
            bool spaceIsSafeCompletionCharacter = !String.IsNullOrEmpty(guidance.Value);

            // If there are no tokens suggested here, return empty completion
            if (guidance.Options == QueryTokenCategory.None)
            {
                return(result);
            }

            // Compute the CurrentCompleteValue, *before* considering values, so value IntelliSense can use them
            string queryWithoutIncompleteValue = queryBeforeCursor;

            if (!queryWithoutIncompleteValue.EndsWith(guidance.Value))
            {
                throw new ArribaException("Error: IntelliSense suggestion couldn't be applied.");
            }
            queryWithoutIncompleteValue = queryWithoutIncompleteValue.Substring(0, queryWithoutIncompleteValue.Length - guidance.Value.Length);

            // If the CurrentIncompleteValue is an explicit column name, remove and re-complete that, also
            if (queryWithoutIncompleteValue.EndsWith("["))
            {
                queryWithoutIncompleteValue = queryWithoutIncompleteValue.Substring(0, queryWithoutIncompleteValue.Length - 1);
            }

            result.Complete   = queryWithoutIncompleteValue;
            result.Incomplete = guidance.Value;

            // Build a ranked list of suggestions - preferred token categories, filtered to the prefix already typed
            List <IntelliSenseItem> suggestions = new List <IntelliSenseItem>();

            if (guidance.Options.HasFlag(QueryTokenCategory.BooleanOperator))
            {
                AddWhenPrefixes(BooleanOperators, guidance.Value, suggestions);
            }

            if (guidance.Options.HasFlag(QueryTokenCategory.CompareOperator))
            {
                AddSuggestionsForCompareOperator(targetTables, lastTerm, guidance, suggestions);
            }

            if (guidance.Options.HasFlag(QueryTokenCategory.ColumnName))
            {
                AddSuggestionsForColumnNames(targetTables, guidance, ref spaceIsSafeCompletionCharacter, suggestions);
            }

            // Space isn't safe to complete values (except when all explicit values shown, bool below)
            if (guidance.Options.HasFlag(QueryTokenCategory.Value))
            {
                spaceIsSafeCompletionCharacter = false;

                AddSuggestionsForTerm(targetTables, result, lastTerm, guidance, suggestions);
            }

            // If *only* a value is valid here, provide a syntax hint for the value type (and reconsider if space is safe to complete)
            if (guidance.Options == QueryTokenCategory.Value)
            {
                AddSuggestionsForValue(targetTables, result, lastTerm, guidance, ref spaceIsSafeCompletionCharacter, suggestions);
            }

            if (guidance.Options.HasFlag(QueryTokenCategory.TermPrefixes))
            {
                AddWhenPrefixes(TermPrefixes, guidance.Value, suggestions);
            }

            // Build a list of valid completion characters
            List <char> completionCharacters = new List <char>();

            completionCharacters.Add('\t');
            if (spaceIsSafeCompletionCharacter)
            {
                completionCharacters.Add(' ');
            }

            // If column names are valid here but term prefixes or compare operators, operator start characters are valid completion characters
            if (guidance.Options.HasFlag(QueryTokenCategory.ColumnName) && !guidance.Options.HasFlag(QueryTokenCategory.CompareOperator) && !guidance.Options.HasFlag(QueryTokenCategory.TermPrefixes))
            {
                completionCharacters.AddRange(ColumnNameCompletionCharacters);
            }

            // If there's only one suggestion and it's been fully typed, remove it
            if (suggestions.Count == 1 && suggestions[0].Display == guidance.Value)
            {
                suggestions.Clear();
            }

            // Finish populating the result
            result.Suggestions          = suggestions;
            result.CompletionCharacters = completionCharacters;

            return(result);
        }