private void BuildExpressionsInternal(JObject where, Junction expressions, string tableAlias, ResolveFieldContext fieldContext, IDictionary <string, string> indexAliases) { foreach (var entry in where.Properties()) { IPredicate expression = null; var values = entry.Name.Split('_', 2); // Gets the full path name without the comparison e.g. aliasPart.alias, not aliasPart.alias_contains. var property = values[0]; // figure out table aliases for collapsed parts and ones with the part suffix removed by the dsl if (tableAlias == null || !tableAlias.EndsWith("Part", StringComparison.OrdinalIgnoreCase)) { var whereArgument = fieldContext?.FieldDefinition?.Arguments.FirstOrDefault(x => x.Name == "where"); if (whereArgument != null) { var whereInput = (WhereInputObjectGraphType)whereArgument.ResolvedType; foreach (var field in whereInput.Fields.Where(x => x.GetMetadata <string>("PartName") != null)) { var partName = field.GetMetadata <string>("PartName"); if ((tableAlias == null && field.GetMetadata <bool>("PartCollapsed") && field.Name.Equals(property, StringComparison.OrdinalIgnoreCase)) || (tableAlias != null && partName.ToFieldName().Equals(tableAlias, StringComparison.OrdinalIgnoreCase))) { tableAlias = indexAliases.TryGetValue(partName, out var indexTableAlias) ? indexTableAlias : tableAlias; break; } } } } if (tableAlias != null) { property = $"{tableAlias}.{property}"; } if (values.Length == 1) { if (string.Equals(values[0], "or", StringComparison.OrdinalIgnoreCase)) { expression = Expression.Disjunction(); BuildWhereExpressions(entry.Value, (Junction)expression, tableAlias, fieldContext, indexAliases); } else if (string.Equals(values[0], "and", StringComparison.OrdinalIgnoreCase)) { expression = Expression.Conjunction(); BuildWhereExpressions(entry.Value, (Junction)expression, tableAlias, fieldContext, indexAliases); } else if (string.Equals(values[0], "not", StringComparison.OrdinalIgnoreCase)) { expression = Expression.Conjunction(); BuildWhereExpressions(entry.Value, (Junction)expression, tableAlias, fieldContext, indexAliases); expression = Expression.Not(expression); } else if (entry.HasValues && entry.Value.Type == JTokenType.Object) { // Loop through the part's properties, passing the name of the part as the table tableAlias. // This tableAlias can then be used with the table alias to index mappings to join with the correct table. BuildWhereExpressions(entry.Value, expressions, values[0], fieldContext, indexAliases); } else { var propertyValue = entry.Value.ToObject <object>(); expression = Expression.Equal(property, propertyValue); } } else { var value = entry.Value.ToObject <object>(); switch (values[1]) { case "not": expression = Expression.Not(Expression.Equal(property, value)); break; case "gt": expression = Expression.GreaterThan(property, value); break; case "gte": expression = Expression.GreaterThanOrEqual(property, value); break; case "lt": expression = Expression.LessThan(property, value); break; case "lte": expression = Expression.LessThanOrEqual(property, value); break; case "contains": expression = Expression.Like(property, (string)value, MatchOptions.Contains); break; case "not_contains": expression = Expression.Not(Expression.Like(property, (string)value, MatchOptions.Contains)); break; case "starts_with": expression = Expression.Like(property, (string)value, MatchOptions.StartsWith); break; case "not_starts_with": expression = Expression.Not(Expression.Like(property, (string)value, MatchOptions.StartsWith)); break; case "ends_with": expression = Expression.Like(property, (string)value, MatchOptions.EndsWith); break; case "not_ends_with": expression = Expression.Not(Expression.Like(property, (string)value, MatchOptions.EndsWith)); break; case "in": expression = Expression.In(property, entry.Value.ToObject <object[]>()); break; case "not_in": expression = Expression.Not(Expression.In(property, entry.Value.ToObject <object[]>())); break; default: expression = Expression.Equal(property, value); break; } } if (expression != null) { expressions.Add(expression); } } }
private async Task <IQuery <ContentItem> > FilterWhereArguments( IQuery <ContentItem, ContentItemIndex> query, JObject where, ResolveFieldContext fieldContext, ISession session, GraphQLContext context) { if (where == null) { return(query); } string defaultTableAlias = query.GetTypeAlias(typeof(ContentItemIndex)); var transaction = await session.DemandAsync(); IPredicateQuery predicateQuery = new PredicateQuery( dialect: session.Store.Configuration.SqlDialect, shellSettings: context.ServiceProvider.GetService <ShellSettings>(), propertyProviders: context.ServiceProvider.GetServices <IIndexPropertyProvider>()); // Create the default table alias predicateQuery.CreateAlias("", nameof(ContentItemIndex)); predicateQuery.CreateTableAlias(nameof(ContentItemIndex), defaultTableAlias); // Add all provided table alias to the current predicate query var providers = context.ServiceProvider.GetServices <IIndexAliasProvider>(); var indexes = new Dictionary <string, IndexAlias>(StringComparer.OrdinalIgnoreCase); var indexAliases = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); foreach (var aliasProvider in providers) { foreach (var alias in aliasProvider.GetAliases()) { predicateQuery.CreateAlias(alias.Alias, alias.Index); indexAliases.Add(alias.Alias, alias.Alias); if (!indexes.ContainsKey(alias.Index)) { indexes.Add(alias.Index, alias); } } } var expressions = Expression.Conjunction(); BuildWhereExpressions(where, expressions, null, fieldContext, indexAliases); expressions.SearchUsedAlias(predicateQuery); // Add all Indexes that were used in the predicate query IQuery <ContentItem> contentQuery = query; foreach (var usedAlias in predicateQuery.GetUsedAliases()) { if (indexes.ContainsKey(usedAlias)) { contentQuery = contentQuery.With(indexes[usedAlias].IndexType); var tableAlias = query.GetTypeAlias(indexes[usedAlias].IndexType); predicateQuery.CreateTableAlias(indexes[usedAlias].Index, tableAlias); } } var whereSqlClause = expressions.ToSqlString(predicateQuery); query = query.Where(whereSqlClause); // Add all parameters that were used in the predicate query foreach (var parameter in predicateQuery.Parameters) { query = query.WithParameter(parameter.Key, parameter.Value); } return(contentQuery); }