public void QueryIntelliSense_TokenOptionWalkthrough() { QueryIntelliSense qi = new QueryIntelliSense(); // This walkthrough checks what IntelliSense thinks while fully typing the query: // "BareValue : Value AnotherValue !([Analyzer] == \"true\" || Something) AND \"Target \"" // It covers: // - BareValue distinguished between column name (after operator) or values (after space, next boolean operator) // - ":" still suggests operator because it could be "::" // - Value distinguished as value (unambiguous because of operator beforehand) until space) // - "AnotherValue" could be "AND" until "O" // - "!" could be "!=" until "(" // - "(" starts new term but no leading boolean operator is valid // - Explicit Column names while unterminated, right after terminator // - "==" still suggests operator after "=" because it could still be "==" // - Explicit Value while unterminated, right after terminator // - Nothing suggested until space typed after quoted value // - "||" still suggests boolean operator after "|" because it could be "||" // - ")" terminates previous value // - "AND" still suggests column name or value until complete with space // - Explicit value without column name beforehand Assert.AreEqual("[] [Term]", qi.GetCurrentTokenOptions("").ToString()); Assert.AreEqual("[BareValue] [ColumnName, Value]", qi.GetCurrentTokenOptions("BareValue").ToString()); Assert.AreEqual("[] [BooleanOperator, CompareOperator, Term]", qi.GetCurrentTokenOptions("BareValue ").ToString()); Assert.AreEqual("[:] [CompareOperator]", qi.GetCurrentTokenOptions("BareValue :").ToString()); Assert.AreEqual("[] [Value]", qi.GetCurrentTokenOptions("BareValue : ").ToString()); Assert.AreEqual("[V] [Value]", qi.GetCurrentTokenOptions("BareValue : V").ToString()); Assert.AreEqual("[Value] [Value]", qi.GetCurrentTokenOptions("BareValue : Value").ToString()); Assert.AreEqual("[] [BooleanOperator, Term]", qi.GetCurrentTokenOptions("BareValue : Value ").ToString()); Assert.AreEqual("[An] [BooleanOperator, ColumnName, Value]", qi.GetCurrentTokenOptions("BareValue : Value An").ToString()); Assert.AreEqual("[AnotherValue] [BooleanOperator, ColumnName, Value]", qi.GetCurrentTokenOptions("BareValue : Value AnotherValue").ToString()); Assert.AreEqual("[] [BooleanOperator, CompareOperator, Term]", qi.GetCurrentTokenOptions("BareValue : Value AnotherValue ").ToString()); Assert.AreEqual("[!] [BooleanOperator, CompareOperator]", qi.GetCurrentTokenOptions("BareValue : Value AnotherValue !").ToString()); Assert.AreEqual("[] [Term]", qi.GetCurrentTokenOptions("BareValue : Value AnotherValue !(").ToString()); Assert.AreEqual("[] [ColumnName]", qi.GetCurrentTokenOptions("BareValue : Value AnotherValue !([").ToString()); Assert.AreEqual("[Analyzer] [ColumnName]", qi.GetCurrentTokenOptions("BareValue : Value AnotherValue !([Analyzer").ToString()); Assert.AreEqual("[] [CompareOperator]", qi.GetCurrentTokenOptions("BareValue : Value AnotherValue !([Analyzer]").ToString()); Assert.AreEqual("[] [CompareOperator]", qi.GetCurrentTokenOptions("BareValue : Value AnotherValue !([Analyzer] ").ToString()); Assert.AreEqual("[=] [CompareOperator]", qi.GetCurrentTokenOptions("BareValue : Value AnotherValue !([Analyzer] =").ToString()); Assert.AreEqual("[==] [CompareOperator]", qi.GetCurrentTokenOptions("BareValue : Value AnotherValue !([Analyzer] ==").ToString()); Assert.AreEqual("[] [Value]", qi.GetCurrentTokenOptions("BareValue : Value AnotherValue !([Analyzer] == ").ToString()); Assert.AreEqual("[] [Value]", qi.GetCurrentTokenOptions("BareValue : Value AnotherValue !([Analyzer] == \"").ToString()); Assert.AreEqual("[tr] [Value]", qi.GetCurrentTokenOptions("BareValue : Value AnotherValue !([Analyzer] == \"tr").ToString()); Assert.AreEqual("[] [None]", qi.GetCurrentTokenOptions("BareValue : Value AnotherValue !([Analyzer] == \"true\"").ToString()); Assert.AreEqual("[] [BooleanOperator, Term]", qi.GetCurrentTokenOptions("BareValue : Value AnotherValue !([Analyzer] == \"true\" ").ToString()); Assert.AreEqual("[|] [BooleanOperator]", qi.GetCurrentTokenOptions("BareValue : Value AnotherValue !([Analyzer] == \"true\" |").ToString()); Assert.AreEqual("[] [Term]", qi.GetCurrentTokenOptions("BareValue : Value AnotherValue !([Analyzer] == \"true\" ||").ToString()); Assert.AreEqual("[] [Term]", qi.GetCurrentTokenOptions("BareValue : Value AnotherValue !([Analyzer] == \"true\" || ").ToString()); Assert.AreEqual("[Some] [ColumnName, Value]", qi.GetCurrentTokenOptions("BareValue : Value AnotherValue !([Analyzer] == \"true\" || Some").ToString()); Assert.AreEqual("[] [BooleanOperator, Term]", qi.GetCurrentTokenOptions("BareValue : Value AnotherValue !([Analyzer] == \"true\" || Something)").ToString()); Assert.AreEqual("[AN] [BooleanOperator, ColumnName, Value]", qi.GetCurrentTokenOptions("BareValue : Value AnotherValue !([Analyzer] == \"true\" || Something) AN").ToString()); Assert.AreEqual("[AND] [BooleanOperator, Term]", qi.GetCurrentTokenOptions("BareValue : Value AnotherValue !([Analyzer] == \"true\" || Something) AND").ToString()); Assert.AreEqual("[] [Term]", qi.GetCurrentTokenOptions("BareValue : Value AnotherValue !([Analyzer] == \"true\" || Something) AND ").ToString()); Assert.AreEqual("[] [Value]", qi.GetCurrentTokenOptions("BareValue : Value AnotherValue !([Analyzer] == \"true\" || Something) AND \"").ToString()); Assert.AreEqual("[Target] [Value]", qi.GetCurrentTokenOptions("BareValue : Value AnotherValue !([Analyzer] == \"true\" || Something) AND \"Target").ToString()); Assert.AreEqual("[Target ] [Value]", qi.GetCurrentTokenOptions("BareValue : Value AnotherValue !([Analyzer] == \"true\" || Something) AND \"Target ").ToString()); Assert.AreEqual("[] [None]", qi.GetCurrentTokenOptions("BareValue : Value AnotherValue !([Analyzer] == \"true\" || Something) AND \"Target \"").ToString()); Assert.AreEqual("[] [BooleanOperator, Term]", qi.GetCurrentTokenOptions("BareValue : Value AnotherValue !([Analyzer] == \"true\" || Something) AND \"Target \" ").ToString()); }
private async Task <IResponse> Suggest(IRequestContext ctx, Route route) { NameValueCollection p = await ParametersFromQueryStringAndBody(ctx); string query = p["q"]; string selectedTable = p["t"]; IPrincipal user = ctx.Request.User; IntelliSenseResult result = null; using (ctx.Monitor(MonitorEventLevel.Verbose, "Suggest", type: "Suggest", detail: query)) { // Get all available tables List <Table> tables = new List <Table>(); foreach (string tableName in this.Database.TableNames) { if (this.HasTableAccess(tableName, user, PermissionScope.Reader)) { if (String.IsNullOrEmpty(selectedTable) || selectedTable.Equals(tableName, StringComparison.OrdinalIgnoreCase)) { tables.Add(this.Database[tableName]); } } } // Get IntelliSense results and return QueryIntelliSense qi = new QueryIntelliSense(); result = qi.GetIntelliSenseItems(query, tables); } return(ArribaResponse.Ok(result)); }
private string CompleteEachKeystroke(string fullQuery) { QueryIntelliSense qi = new QueryIntelliSense(); string query = ""; IntelliSenseResult result = qi.GetIntelliSenseItems(query, this.Tables); foreach (char c in fullQuery) { if (result.CompletionCharacters.Contains(c)) { query = qi.CompleteQuery(query, result, (result.Suggestions.Count > 0 ? result.Suggestions[0] : null), c); } else { query += c; } result = qi.GetIntelliSenseItems(query, this.Tables); } return(query); }
public void QueryIntelliSense_TokenOptionBasics() { QueryIntelliSense p = new QueryIntelliSense(); // "[An" -> ColumnName only [explicit column name escaping] Assert.AreEqual("[An] [ColumnName]", p.GetCurrentTokenOptions("[An").ToString()); // "\"An" -> Value only [explicit value escaping] Assert.AreEqual("[An] [Value]", p.GetCurrentTokenOptions("\"An").ToString()); // "[Analyzer]" -> CompareOperator [column name closed by ']'] Assert.AreEqual("[] [CompareOperator]", p.GetCurrentTokenOptions("[Analyzer]").ToString()); Assert.AreEqual("[] [CompareOperator]", p.GetCurrentTokenOptions("[Analyzer] ").ToString()); // "\"Analysis\"" -> BooleanOperator, Term [a bare term explicitly closed, *only after space*] Assert.AreEqual("[] [None]", p.GetCurrentTokenOptions("\"Analysis\"").ToString()); Assert.AreEqual("[] [BooleanOperator, Term]", p.GetCurrentTokenOptions("\"Analysis\" ").ToString()); // "Analyzer:" -> Operator [suggest operators until space afterward] Assert.AreEqual("[:] [CompareOperator]", p.GetCurrentTokenOptions("Analyzer:").ToString()); Assert.AreEqual("[::] [CompareOperator]", p.GetCurrentTokenOptions("Analyzer::").ToString()); Assert.AreEqual("[] [Value]", p.GetCurrentTokenOptions("Analyzer: ").ToString()); Assert.AreEqual("[V] [Value]", p.GetCurrentTokenOptions("Analyzer:V").ToString()); // Multi-character operator prefixes *right at end* should still suggest operators // "Analyzer >" -> CompareOperator Assert.AreEqual("[>] [CompareOperator]", p.GetCurrentTokenOptions("Analyzer >").ToString()); Assert.AreEqual("[>] [CompareOperator]", p.GetCurrentTokenOptions("[Analyzer] >").ToString()); Assert.AreEqual("[!] [BooleanOperator, CompareOperator]", p.GetCurrentTokenOptions("Analyzer !").ToString()); Assert.AreEqual("[!] [CompareOperator]", p.GetCurrentTokenOptions("[Analyzer] !").ToString()); Assert.AreEqual("[|] [BooleanOperator, CompareOperator]", p.GetCurrentTokenOptions("Analyzer |").ToString()); Assert.AreEqual("[|] [CompareOperator]", p.GetCurrentTokenOptions("[Analyzer] |").ToString()); Assert.AreEqual("[&] [BooleanOperator]", p.GetCurrentTokenOptions("Analyzer &").ToString()); Assert.AreEqual("[&] [BooleanOperator]", p.GetCurrentTokenOptions("[Analyzer] &").ToString()); // Multi-character operator prefix with trailing space means it's not a comparison. Assert.AreEqual("[] [Value]", p.GetCurrentTokenOptions("Analyzer > ").ToString()); Assert.AreEqual("[] [Value]", p.GetCurrentTokenOptions("[Analyzer] > ").ToString()); // Trailing '|' will be parsed as 'OR' but no new term to indicate situation Assert.AreEqual("[] [Term]", p.GetCurrentTokenOptions("Analyzer | ").ToString()); Assert.AreEqual("[] [Term]", p.GetCurrentTokenOptions("[Analyzer] | ").ToString()); // Trailing '!' is a negation on the next term. Should suggest term starts Assert.AreEqual("[] [Term]", p.GetCurrentTokenOptions("Analyzer ! ").ToString()); Assert.AreEqual("[] [Term]", p.GetCurrentTokenOptions("[Analyzer] ! ").ToString()); // Trailing '&' will be parsed as 'AND' but no new term to indicate situation Assert.AreEqual("[] [Term]", p.GetCurrentTokenOptions("Analyzer & ").ToString()); Assert.AreEqual("[] [Term]", p.GetCurrentTokenOptions("[Analyzer] & ").ToString()); // Explicit column name without operator or value turns into [Column] != "" Assert.AreEqual("[BareTerm] [BooleanOperator, ColumnName, Value]", p.GetCurrentTokenOptions("[Analyzer] BareTerm").ToString()); Assert.AreEqual("[] [None]", p.GetCurrentTokenOptions("[Analyzer] \"QuotedValue\"").ToString()); Assert.AreEqual("[] [BooleanOperator, Term]", p.GetCurrentTokenOptions("[Analyzer] \"QuotedValue\" ").ToString()); // Invalid Queries Assert.AreEqual("[] [None]", p.GetCurrentTokenOptions("\"Analysis\"=\"Interesting\"").ToString()); }
public void QueryIntelliSense_InlineInsights() { QueryIntelliSense qi = new QueryIntelliSense(); IntelliSenseResult result; // Tables without data don't suggest values result = qi.GetIntelliSenseItems("[City] = ", Tables); Assert.AreEqual(0, result.Suggestions.Count); // Unique values aren't suggested result = qi.GetIntelliSenseItems("[ID] = ", Tables); Assert.AreEqual(0, result.Suggestions.Count); // Values are suggested in order by frequency result = qi.GetIntelliSenseItems("[Student Count] = ", Tables); Assert.AreEqual($"100000 {ToPercent(.7)}, 10000 {ToPercent(.2)}, 1000 {ToPercent(.1)}", ItemsAndCounts(result)); // Values are filtered according to the query result = qi.GetIntelliSenseItems("[ID] < 15 AND [Student Count] = ", Tables); Assert.AreEqual($"1000 {ToPercent(.67)}, 10000 {ToPercent(.33)}", ItemsAndCounts(result)); // Distributions are returned for range operators // Only non-empty buckets are returned. result = qi.GetIntelliSenseItems("[Student Count] < ", Tables); Assert.AreEqual($"17500 {ToPercent(.3)}", ItemsAndCounts(result)); result = qi.GetIntelliSenseItems("[Student Count] <= ", Tables); Assert.AreEqual($"1000 {ToPercent(.1)}, 17500 {ToPercent(.3)}, 100000 all", ItemsAndCounts(result)); result = qi.GetIntelliSenseItems("[Student Count] > ", Tables); Assert.AreEqual($"83500 {ToPercent(.7)}, 1000 {ToPercent(.9)}", ItemsAndCounts(result)); result = qi.GetIntelliSenseItems("[Student Count] >= ", Tables); Assert.AreEqual($"100000 {ToPercent(.7)}, 1000 all", ItemsAndCounts(result)); // Only show one value when there's only one value available result = qi.GetIntelliSenseItems("[ID] > 50 AND [Student Count] >= ", Tables); Assert.AreEqual("100000 all", ItemsAndCounts(result)); // Only provide type hint when no rows match the query result = qi.GetIntelliSenseItems("[ID] < 0 AND [Student Count] >= ", Tables); Assert.AreEqual(0, result.Suggestions.Count); // Works for TimeSpan result = qi.GetIntelliSenseItems("[SchoolYearLength] >= ", Tables); Assert.AreEqual($"211.00:00:00 {ToPercent(.12)}, 208.00:00:00 {ToPercent(.21)}, 204.00:00:00 {ToPercent(.33)}, 200.00:00:00 {ToPercent(.45)}, 196.00:00:00 {ToPercent(.57)}, 192.00:00:00 {ToPercent(.72)}, 188.00:00:00 {ToPercent(.88)}", ItemsAndCounts(result)); // Works for DateTime result = qi.GetIntelliSenseItems("[WhenFounded] >= ", Tables); Assert.AreEqual(7, result.Suggestions.Count); // Only provide type hint (and no error) for unsupported type result = qi.GetIntelliSenseItems("[ID] < 0 AND [Name] >= ", Tables); Assert.AreEqual(0, result.Suggestions.Count); // Term column suggestions are offered result = qi.GetIntelliSenseItems("Uni", Tables); Assert.AreEqual($"[Name] : Uni {ToPercent(.40)}, [Mascot] : Uni {ToPercent(.25)}", ItemsAndCounts(result)); // Term column suggestions are based on the remaining query rows result = qi.GetIntelliSenseItems("[Mascot] : Uni AND Uni", Tables); Assert.AreEqual($"[Mascot] : Uni all, [Name] : Uni {ToPercent(.40)}", ItemsAndCounts(result)); // Term suggestions only show for columns which have any matches result = qi.GetIntelliSenseItems("Ele", Tables); Assert.AreEqual($"[Mascot] : Ele {ToPercent(.25)}", ItemsAndCounts(result)); // Term suggestions only show if the term has matches result = qi.GetIntelliSenseItems("Elelion", Tables); Assert.AreEqual("", ItemsAndCounts(result)); // Term suggestions only show if remaining terms have matches result = qi.GetIntelliSenseItems("[ID] < 0 AND Uni", Tables); Assert.AreEqual("", ItemsAndCounts(result)); }
public void QueryIntelliSense_GetIntelliSenseItems_Basic() { QueryIntelliSense qi = new QueryIntelliSense(); IntelliSenseResult result; // ""Name" = "Na" suggests nothing (invalid query) result = qi.GetIntelliSenseItems("\"Name\" = \"Na", Tables); Assert.AreEqual(0, result.Suggestions.Count); // No Query: ColumnNames, alphabetical, with no duplicates, then bare value, then TermPrefixes string allColumnNamesOrTerm = "[Age], [City], [ID], [Mascot], [Name], [SchoolHasMascot], [SchoolYearLength], [Student Count], [WhenFounded], [*], !, ("; result = qi.GetIntelliSenseItems("", Tables); Assert.AreEqual(allColumnNamesOrTerm, string.Join(", ", result.Suggestions.Select(ii => ii.Display))); Assert.AreEqual("", result.SyntaxHint); Assert.AreEqual("", result.Incomplete); Assert.AreEqual("", result.Complete); Assert.AreEqual("", result.Query); // No Query, one table: ColumnNames for single table, then bare value, then TermPrefixes string studentTableNamesOrTerm = "[Age], [City], [ID], [Name], [*], !, ("; result = qi.GetIntelliSenseItems("", new List <Table>() { Student }); Assert.AreEqual(studentTableNamesOrTerm, string.Join(", ", result.Suggestions.Select(ii => ii.Display))); Assert.AreEqual("", result.SyntaxHint); Assert.AreEqual("", result.Incomplete); Assert.AreEqual("", result.Complete); Assert.AreEqual("", result.Query); // No Query, no tables: no response result = qi.GetIntelliSenseItems(null, Tables); Assert.AreEqual(0, result.Suggestions.Count); result = qi.GetIntelliSenseItems("", new List <Table>() { }); Assert.AreEqual(0, result.Suggestions.Count); result = qi.GetIntelliSenseItems("", null); Assert.AreEqual(0, result.Suggestions.Count); // "Age > 10 AND SchoolHasMascot = true AND " suggests nothing (no tables have both columns) result = qi.GetIntelliSenseItems("Age > 10 AND SchoolHasMascot = true AND ", new List <Table>() { Student }); Assert.AreEqual(0, result.Suggestions.Count); // "Name: hey AND " suggests all table columns (no tables excludable yet) result = qi.GetIntelliSenseItems("Name: hey AND ", Tables); Assert.AreEqual(allColumnNamesOrTerm, string.Join(", ", result.Suggestions.Select(ii => ii.Display))); Assert.AreEqual("", result.Incomplete); // "Age > 10 AND (" suggests student table columns only (no last term) result = qi.GetIntelliSenseItems("Age > 10 AND ( ", Tables); Assert.AreEqual(studentTableNamesOrTerm, string.Join(", ", result.Suggestions.Select(ii => ii.Display))); Assert.AreEqual("Age > 10 AND ( ", result.Complete); Assert.AreEqual("", result.Incomplete); // "* : 10 AND " suggests all column only ('*' doesn't filter anything) result = qi.GetIntelliSenseItems("* : 10 AND ", Tables); Assert.AreEqual(allColumnNamesOrTerm, string.Join(", ", result.Suggestions.Select(ii => ii.Display))); // "[Na" must be a column name, and one of the 'Name' ones // CurrentIncompleteValue is just the bare column name (no '[') for list filtering, but CurrentCompleteValue is "", so the "[" is replaced by the completion. result = qi.GetIntelliSenseItems("[Na", Tables); Assert.AreEqual("[Name]", string.Join(", ", result.Suggestions.Select(ii => ii.Display))); Assert.AreEqual("[Name] | <Multiple Tables> | ColumnName | [Name]", string.Join(", ", result.Suggestions)); Assert.AreEqual("Na", result.Incomplete); Assert.AreEqual("", result.Complete); Assert.AreEqual("[Na", result.Query); // "[SchoolSumm" should suggest nothing (no remaining column names) result = qi.GetIntelliSenseItems("[SchoolSumm", Tables); Assert.AreEqual("", string.Join(", ", result.Suggestions.Select(ii => ii.Display))); // "[Name] " should suggest operators for string result = qi.GetIntelliSenseItems("[Name] ", Tables); Assert.AreEqual(string.Join(", ", QueryIntelliSense.CompareOperatorsForString.Select(ii => ii.Display)), string.Join(", ", result.Suggestions.Select(ii => ii.Display))); Assert.AreEqual("", result.Incomplete); Assert.AreEqual("[Name] ", result.Query); // "[Name] = " should suggest value but doesn't know the type (multiple matches) result = qi.GetIntelliSenseItems("[Name] = ", Tables); Assert.AreEqual(QueryIntelliSense.Value, result.SyntaxHint); Assert.AreEqual("", result.Incomplete); // "[Student " should match 'Student Count' (space in column filtering is correct) result = qi.GetIntelliSenseItems("[Student ", Tables); Assert.AreEqual("[Student Count]", string.Join(", ", result.Suggestions.Select(ii => ii.Display))); Assert.AreEqual("Student ", result.Incomplete); // "[Student " should not match 'Student Count' (second space means non-match) result = qi.GetIntelliSenseItems("[Student ", Tables); Assert.AreEqual("", string.Join(", ", result.Suggestions.Select(ii => ii.Display))); Assert.AreEqual("Student ", result.Incomplete); // "[Student Count]" should suggest operators for numbers result = qi.GetIntelliSenseItems("[Student Count]", Tables); Assert.AreEqual(string.Join(", ", QueryIntelliSense.CompareOperatorsForOther.Select(ii => ii.Display)), string.Join(", ", result.Suggestions.Select(ii => ii.Display))); // "Age > " suggests TimeSpans result = qi.GetIntelliSenseItems("Age > ", Tables); Assert.AreEqual(QueryIntelliSense.TimeSpanValue, result.SyntaxHint); // "WhenFounded > " suggests DateTimes result = qi.GetIntelliSenseItems("WhenFounded <= ", Tables); Assert.AreEqual(QueryIntelliSense.DateTimeValue, result.SyntaxHint); Assert.AreEqual("WhenFounded <= ", result.Complete); Assert.AreEqual("", result.Incomplete); // "[SchoolHasMascot] " suggests boolean operators result = qi.GetIntelliSenseItems("[SchoolHasMascot] ", Tables); Assert.AreEqual(string.Join(", ", QueryIntelliSense.CompareOperatorsForBoolean.Select(ii => ii.Display)), string.Join(", ", result.Suggestions.Select(ii => ii.Display))); // "SchoolHasMascot = " suggests booleans // It suggests 'True' first because more rows contain true result = qi.GetIntelliSenseItems("SchoolHasMascot = ", Tables); Assert.AreEqual("True, False", string.Join(", ", result.Suggestions.Select(ii => ii.Display))); // "City : " suggests string result = qi.GetIntelliSenseItems("City : ", Tables); Assert.AreEqual(QueryIntelliSense.StringValue, result.SyntaxHint); // "[Student Count] < " suggests that type result = qi.GetIntelliSenseItems("[Student Count] < ", Tables); Assert.AreEqual(QueryIntelliSense.IntegerValue, result.SyntaxHint); // "[Student Count] < 7000 " suggests boolean, columns, value result = qi.GetIntelliSenseItems("[Student Count] < 7000 ", Tables); Assert.AreEqual(string.Join(", ", QueryIntelliSense.BooleanOperators.Select(ii => ii.Display)), string.Join(", ", result.Suggestions.Where(ii => ii.Category == QueryTokenCategory.BooleanOperator).Select(ii => ii.Display))); // "[Student Count] < 7000 &&" suggests column, value, term prefix string collegeNamesOrTerm = "[ID], [Mascot], [Name], [SchoolHasMascot], [SchoolYearLength], [Student Count], [WhenFounded], [*], !, ("; result = qi.GetIntelliSenseItems("[Student Count] < 7000 &&", Tables); Assert.AreEqual(collegeNamesOrTerm, string.Join(", ", result.Suggestions.Select(ii => ii.Display))); // "[Name] : Hey A" shows both "AND", "Age" (either valid at this point) result = qi.GetIntelliSenseItems("[Name] : Hey A", Tables); Assert.AreEqual("AND, [Age]", string.Join(", ", result.Suggestions.Select(ii => ii.Display))); }