RDFConstructQuery is the SPARQL "CONSTRUCT" query implementation.
        /// <summary>
        /// Get the result table of the given pattern group
        /// </summary>
        internal static void CombinePatterns(RDFConstructQuery query, RDFPatternGroup patternGroup)
        {
            if (patternGroup.Patterns.Any())
            {
                //Populate pattern group result table
                DataTable patternGroupResultTable = RDFQueryEngine.CombineTables(query.PatternResultTables[patternGroup], false);

                //Add it to the list of pattern group result tables
                query.PatternGroupResultTables.Add(patternGroup, patternGroupResultTable);

                //Populate its metadata
                query.PatternGroupResultTables[patternGroup].TableName = patternGroup.ToString();
                if (!query.PatternGroupResultTables[patternGroup].ExtendedProperties.ContainsKey("IsOptional"))
                {
                    query.PatternGroupResultTables[patternGroup].ExtendedProperties.Add("IsOptional", patternGroup.IsOptional);
                }
                else
                {
                    query.PatternGroupResultTables[patternGroup].ExtendedProperties["IsOptional"] = patternGroup.IsOptional;
                }
                if (!query.PatternGroupResultTables[patternGroup].ExtendedProperties.ContainsKey("JoinAsUnion"))
                {
                    query.PatternGroupResultTables[patternGroup].ExtendedProperties.Add("JoinAsUnion", patternGroup.JoinAsUnion);
                }
                else
                {
                    query.PatternGroupResultTables[patternGroup].ExtendedProperties["JoinAsUnion"] = patternGroup.JoinAsUnion;
                }
            }
        }
        /// <summary>
        /// Apply the filters of the given pattern group to its result table
        /// </summary>
        internal static void ApplyFilters(RDFConstructQuery query, RDFPatternGroup patternGroup)
        {
            if (patternGroup.Patterns.Any() && patternGroup.Filters.Any())
            {
                DataTable   filteredTable = query.PatternGroupResultTables[patternGroup].Clone();
                IEnumerator rowsEnum      = query.PatternGroupResultTables[patternGroup].Rows.GetEnumerator();

                //Iterate the rows of the pattern group's result table
                Boolean keepRow = false;
                while (rowsEnum.MoveNext())
                {
                    //Apply the pattern group's filters on the row
                    keepRow = true;
                    IEnumerator <RDFFilter> filtersEnum = patternGroup.Filters.GetEnumerator();
                    while (keepRow && filtersEnum.MoveNext())
                    {
                        keepRow = filtersEnum.Current.ApplyFilter((DataRow)rowsEnum.Current, false);
                    }

                    //If the row has passed all the filters, keep it in the filtered result table
                    if (keepRow)
                    {
                        DataRow newRow = filteredTable.NewRow();
                        newRow.ItemArray = ((DataRow)rowsEnum.Current).ItemArray;
                        filteredTable.Rows.Add(newRow);
                    }
                }

                //Save the result datatable
                query.PatternGroupResultTables[patternGroup] = filteredTable;
            }
        }
        /// <summary>
        /// Apply the filters of the given pattern group to its result table
        /// </summary>
        internal static void ApplyFilters(RDFConstructQuery query, RDFPatternGroup patternGroup) {
            if (patternGroup.Patterns.Any() && patternGroup.Filters.Any()) {
                DataTable filteredTable  = query.PatternGroupResultTables[patternGroup].Clone();
                IEnumerator rowsEnum     = query.PatternGroupResultTables[patternGroup].Rows.GetEnumerator();
                
                //Iterate the rows of the pattern group's result table
                Boolean keepRow          = false;
                while (rowsEnum.MoveNext()) {

                    //Apply the pattern group's filters on the row
                    keepRow              = true;
                    IEnumerator<RDFFilter> filtersEnum       = patternGroup.Filters.GetEnumerator();
                    while (keepRow      && filtersEnum.MoveNext()) {
                        keepRow          = filtersEnum.Current.ApplyFilter((DataRow)rowsEnum.Current, false);
                    }

                    //If the row has passed all the filters, keep it in the filtered result table
                    if (keepRow) {
                        DataRow newRow   = filteredTable.NewRow();
                        newRow.ItemArray = ((DataRow)rowsEnum.Current).ItemArray;
                        filteredTable.Rows.Add(newRow);
                    }

                }

                //Save the result datatable
                query.PatternGroupResultTables[patternGroup] = filteredTable;
            }
        }
        /// <summary>
        /// Apply the query modifiers to the query result table
        /// </summary>
        internal static DataTable ApplyModifiers(RDFConstructQuery query, DataTable table)
        {
            String tablenameBak = table.TableName;

            //Apply the DISTINCT modifier
            table = new RDFDistinctModifier().ApplyModifier(table);

            //Apply the OFFSET modifier
            table = query.Modifiers.Where(m => m is RDFOffsetModifier)
                    .Aggregate(table, (current, modifier) => modifier.ApplyModifier(current));

            //Apply the LIMIT modifier
            table = query.Modifiers.Where(m => m is RDFLimitModifier)
                    .Aggregate(table, (current, modifier) => modifier.ApplyModifier(current));

            table.TableName = tablenameBak;
            return(table);
        }
        /// <summary>
        /// Get the intermediate result tables of the given pattern group
        /// </summary>
        internal static void EvaluatePatterns(RDFConstructQuery query, RDFPatternGroup patternGroup, Object graphOrStore)
        {
            query.PatternResultTables[patternGroup] = new List <DataTable>();

            //Iterate over the patterns of the pattern group
            foreach (RDFPattern pattern in patternGroup.Patterns)
            {
                //Apply the pattern to the graph/store
                DataTable patternResultsTable = graphOrStore is RDFGraph?RDFQueryEngine.ApplyPattern(pattern, (RDFGraph)graphOrStore)
                                                    : RDFQueryEngine.ApplyPattern(pattern, (RDFStore)graphOrStore);

                //Set the name and the optionality metadata of the result datatable
                patternResultsTable.TableName = pattern.ToString();
                patternResultsTable.ExtendedProperties.Add("IsOptional", pattern.IsOptional);
                patternResultsTable.ExtendedProperties.Add("JoinAsUnion", pattern.JoinAsUnion);

                //Save the result datatable
                query.PatternResultTables[patternGroup].Add(patternResultsTable);
            }
        }
        /// <summary>
        /// Get the intermediate result tables of the given pattern group
        /// </summary>
        internal static void EvaluatePatterns(RDFConstructQuery query, RDFPatternGroup patternGroup, Object graphOrStore) {
            query.PatternResultTables[patternGroup] = new List<DataTable>();

            //Iterate over the patterns of the pattern group
            foreach (RDFPattern pattern in patternGroup.Patterns) {

                //Apply the pattern to the graph/store
                DataTable patternResultsTable       = graphOrStore is RDFGraph ? RDFQueryEngine.ApplyPattern(pattern, (RDFGraph)graphOrStore)
                                                                               : RDFQueryEngine.ApplyPattern(pattern, (RDFStore)graphOrStore);

                //Set the name and the optionality metadata of the result datatable
                patternResultsTable.TableName       = pattern.ToString();
                patternResultsTable.ExtendedProperties.Add("IsOptional",  pattern.IsOptional);
                patternResultsTable.ExtendedProperties.Add("JoinAsUnion", pattern.JoinAsUnion);

                //Save the result datatable
                query.PatternResultTables[patternGroup].Add(patternResultsTable);

            }
        }
示例#7
0
        /// <summary>
        /// Prints the string representation of a SPARQL CONSTRUCT query
        /// </summary>
        internal static String PrintConstructQuery(RDFConstructQuery constructQuery)
        {
            StringBuilder sb = new StringBuilder();

            if (constructQuery != null)
            {
                #region PREFIXES
                if (constructQuery.Prefixes.Any())
                {
                    constructQuery.Prefixes.ForEach(pf =>
                    {
                        sb.Append("PREFIX " + pf.NamespacePrefix + ": <" + pf.NamespaceUri + ">\n");
                    });
                    sb.Append("\n");
                }
                #endregion

                #region HEADER

                #region BEGINCONSTRUCT
                sb.Append("CONSTRUCT");
                #endregion

                #region TEMPLATES
                sb.Append("\n{\n");
                constructQuery.Templates.ForEach(tp =>
                {
                    String tpString = PrintPattern(tp, constructQuery.Prefixes);

                    //Remove the Context from the template print (since it is not supported by CONSTRUCT query)
                    if (tp.Context != null)
                    {
                        tpString = tpString.Replace("GRAPH " + tp.Context + " { ", String.Empty).TrimEnd(new Char[] { ' ', '}' });
                    }

                    //Remove the Optional indicator from the template print (since it is not supported by CONSTRUCT query)
                    if (tp.IsOptional)
                    {
                        tpString = tpString.Replace("OPTIONAL { ", String.Empty).TrimEnd(new Char[] { ' ', '}' });
                    }

                    sb.Append("  " + tpString + " .\n");
                });
                sb.Append("}\n");
                #endregion

                #endregion

                #region BODY
                sb.Append("WHERE {\n");

                #region MEMBERS
                Boolean printingUnion = false;
                List <RDFQueryMember> evaluableQueryMembers = constructQuery.GetEvaluableQueryMembers().ToList();
                RDFQueryMember        lastQueryMbr          = evaluableQueryMembers.LastOrDefault();
                foreach (var queryMember in evaluableQueryMembers)
                {
                    #region PATTERNGROUPS
                    if (queryMember is RDFPatternGroup)
                    {
                        //Current pattern group is set as UNION with the next one
                        if (((RDFPatternGroup)queryMember).JoinAsUnion)
                        {
                            //Current pattern group IS NOT the last of the query
                            //(so UNION keyword must be appended at last)
                            if (!queryMember.Equals(lastQueryMbr))
                            {
                                //Begin a new Union block
                                if (!printingUnion)
                                {
                                    printingUnion = true;
                                    sb.Append("  {\n");
                                }
                                sb.Append(PrintPatternGroup((RDFPatternGroup)queryMember, 2, true, constructQuery.Prefixes));
                                sb.Append("    UNION\n");
                            }

                            //Current pattern group IS the last of the query
                            //(so UNION keyword must not be appended at last)
                            else
                            {
                                //End the Union block
                                if (printingUnion)
                                {
                                    printingUnion = false;
                                    sb.Append(PrintPatternGroup((RDFPatternGroup)queryMember, 2, true, constructQuery.Prefixes));
                                    sb.Append("  }\n");
                                }
                                else
                                {
                                    sb.Append(PrintPatternGroup((RDFPatternGroup)queryMember, 0, false, constructQuery.Prefixes));
                                }
                            }
                        }

                        //Current pattern group is set as INTERSECT with the next one
                        else
                        {
                            //End the Union block
                            if (printingUnion)
                            {
                                printingUnion = false;
                                sb.Append(PrintPatternGroup((RDFPatternGroup)queryMember, 2, true, constructQuery.Prefixes));
                                sb.Append("  }\n");
                            }
                            else
                            {
                                sb.Append(PrintPatternGroup((RDFPatternGroup)queryMember, 0, false, constructQuery.Prefixes));
                            }
                        }
                    }
                    #endregion

                    #region SUBQUERY
                    else if (queryMember is RDFQuery)
                    {
                        //Merge main query prefixes
                        constructQuery.Prefixes.ForEach(pf1 => {
                            if (!((RDFSelectQuery)queryMember).Prefixes.Any(pf2 => pf2.Equals(pf1)))
                            {
                                ((RDFSelectQuery)queryMember).AddPrefix(pf1);
                            }
                        });
                        //Current subquery is set as UNION with the next one
                        if (((RDFSelectQuery)queryMember).JoinAsUnion)
                        {
                            //Current subquery IS NOT the last of the query
                            //(so UNION keyword must be appended at last)
                            if (!queryMember.Equals(lastQueryMbr))
                            {
                                //Begin a new Union block
                                if (!printingUnion)
                                {
                                    printingUnion = true;
                                    sb.Append("  {\n");
                                }
                                sb.Append(PrintSelectQuery((RDFSelectQuery)queryMember, 1, true));
                                sb.Append("    UNION\n");
                            }

                            //Current query IS the last of the query
                            //(so UNION keyword must not be appended at last)
                            else
                            {
                                //End the Union block
                                if (printingUnion)
                                {
                                    printingUnion = false;
                                    sb.Append(PrintSelectQuery((RDFSelectQuery)queryMember, 1, true));
                                    sb.Append("  }\n");
                                }
                                else
                                {
                                    sb.Append(PrintSelectQuery((RDFSelectQuery)queryMember, 1, false));
                                }
                            }
                        }

                        //Current query is set as INTERSECT with the next one
                        else
                        {
                            //End the Union block
                            if (printingUnion)
                            {
                                printingUnion = false;
                                sb.Append(PrintSelectQuery((RDFSelectQuery)queryMember, 1, true));
                                sb.Append("  }\n");
                            }
                            else
                            {
                                sb.Append(PrintSelectQuery((RDFSelectQuery)queryMember, 1, false));
                            }
                        }
                    }
                    #endregion
                }
                #endregion

                sb.Append("}");
                #endregion

                #region FOOTER

                #region MODIFIERS
                List <RDFModifier> modifiers = constructQuery.GetModifiers().ToList();

                // LIMIT/OFFSET
                if (modifiers.Any(mod => mod is RDFLimitModifier || mod is RDFOffsetModifier))
                {
                    modifiers.Where(mod => mod is RDFLimitModifier)
                    .ToList()
                    .ForEach(lim => { sb.Append("\n"); sb.Append(lim); });
                    modifiers.Where(mod => mod is RDFOffsetModifier)
                    .ToList()
                    .ForEach(off => { sb.Append("\n"); sb.Append(off); });
                }
                #endregion

                #endregion
            }
            return(sb.ToString());
        }
        /// <summary>
        /// Fills the templates of the given query with data from the given result table
        /// </summary>
        internal static DataTable FillTemplates(RDFConstructQuery constructQuery, DataTable resultTable)
        {
            //Create the structure of the result datatable
            DataTable result = new DataTable("CONSTRUCT_RESULTS");

            result.Columns.Add("SUBJECT", Type.GetType("System.String"));
            result.Columns.Add("PREDICATE", Type.GetType("System.String"));
            result.Columns.Add("OBJECT", Type.GetType("System.String"));
            result.AcceptChanges();

            //Initialize working variables
            Dictionary <String, String> constructRow = new Dictionary <String, String>();

            constructRow.Add("SUBJECT", null);
            constructRow.Add("PREDICATE", null);
            constructRow.Add("OBJECT", null);

            //Iterate on the templates
            foreach (RDFPattern tp in constructQuery.Templates.Where(tp => tp.Variables.Count == 0 ||
                                                                     tp.Variables.TrueForAll(v => resultTable.Columns.Contains(v.ToString()))))
            {
                #region GROUND TEMPLATE
                //Check if the template is ground, so represents an explicit triple to be added as-is
                if (tp.Variables.Count == 0)
                {
                    constructRow["SUBJECT"]   = tp.Subject.ToString();
                    constructRow["PREDICATE"] = tp.Predicate.ToString();
                    constructRow["OBJECT"]    = tp.Object.ToString();
                    RDFQueryUtilities.AddRow(result, constructRow);
                    continue;
                }
                #endregion

                #region NON-GROUND TEMPLATE
                //Iterate the result datatable's rows to construct the triples of the current template
                IEnumerator rowsEnum = resultTable.Rows.GetEnumerator();
                while (rowsEnum.MoveNext())
                {
                    #region SUBJECT
                    //Subject of the template is a variable
                    if (tp.Subject is RDFVariable)
                    {
                        //Check if the template must be skipped, in order to not produce illegal triples
                        //Row contains an unbound value in position of the variable corresponding to the template subject
                        if (((DataRow)rowsEnum.Current).IsNull(tp.Subject.ToString()))
                        {
                            continue;
                        }

                        RDFPatternMember subj = RDFQueryUtilities.ParseRDFPatternMember(((DataRow)rowsEnum.Current)[tp.Subject.ToString()].ToString());
                        //Row contains a literal in position of the variable corresponding to the template subject
                        if (subj is RDFLiteral)
                        {
                            continue;
                        }
                        //Row contains a resource in position of the variable corresponding to the template subject
                        constructRow["SUBJECT"] = subj.ToString();
                    }
                    //Subject of the template is a resource
                    else
                    {
                        constructRow["SUBJECT"] = tp.Subject.ToString();
                    }
                    #endregion

                    #region PREDICATE
                    //Predicate of the template is a variable
                    if (tp.Predicate is RDFVariable)
                    {
                        //Check if the template must be skipped, in order to not produce illegal triples
                        //Row contains an unbound value in position of the variable corresponding to the template predicate
                        if (((DataRow)rowsEnum.Current).IsNull(tp.Predicate.ToString()))
                        {
                            continue;
                        }
                        RDFPatternMember pred = RDFQueryUtilities.ParseRDFPatternMember(((DataRow)rowsEnum.Current)[tp.Predicate.ToString()].ToString());
                        //Row contains a blank resource or a literal in position of the variable corresponding to the template predicate
                        if ((pred is RDFResource && ((RDFResource)pred).IsBlank) || pred is RDFLiteral)
                        {
                            continue;
                        }
                        //Row contains a non-blank resource in position of the variable corresponding to the template predicate
                        constructRow["PREDICATE"] = pred.ToString();
                    }
                    //Predicate of the template is a resource
                    else
                    {
                        constructRow["PREDICATE"] = tp.Predicate.ToString();
                    }
                    #endregion

                    #region OBJECT
                    //Object of the template is a variable
                    if (tp.Object is RDFVariable)
                    {
                        //Check if the template must be skipped, in order to not produce illegal triples
                        //Row contains an unbound value in position of the variable corresponding to the template object
                        if (((DataRow)rowsEnum.Current).IsNull(tp.Object.ToString()))
                        {
                            continue;
                        }
                        RDFPatternMember obj = RDFQueryUtilities.ParseRDFPatternMember(((DataRow)rowsEnum.Current)[tp.Object.ToString()].ToString());
                        //Row contains a resource or a literal in position of the variable corresponding to the template object
                        constructRow["OBJECT"] = obj.ToString();
                    }
                    //Object of the template is a resource or a literal
                    else
                    {
                        constructRow["OBJECT"] = tp.Object.ToString();
                    }
                    #endregion

                    //Insert the triple into the final table
                    RDFQueryUtilities.AddRow(result, constructRow);
                }
                #endregion
            }

            return(result);
        }
        /// <summary>
        /// Apply the query modifiers to the query result table
        /// </summary>
        internal static DataTable ApplyModifiers(RDFConstructQuery query, DataTable table) {
            String tablenameBak = table.TableName;

            //Apply the DISTINCT modifier
            table               = new RDFDistinctModifier().ApplyModifier(table);

            //Apply the OFFSET modifier
            table               = query.Modifiers.Where(m => m is RDFOffsetModifier)
                                                 .Aggregate(table, (current, modifier) => modifier.ApplyModifier(current));

            //Apply the LIMIT modifier
            table               = query.Modifiers.Where(m => m is RDFLimitModifier)
                                                 .Aggregate(table, (current, modifier) => modifier.ApplyModifier(current));

            table.TableName     = tablenameBak;
            return table;
        }
        /// <summary>
        /// Fills the templates of the given query with data from the given result table
        /// </summary>
        internal static DataTable FillTemplates(RDFConstructQuery constructQuery, DataTable resultTable) {

            //Create the structure of the result datatable
            DataTable result = new DataTable("CONSTRUCT_RESULTS");
            result.Columns.Add("SUBJECT",   Type.GetType("System.String"));
            result.Columns.Add("PREDICATE", Type.GetType("System.String"));
            result.Columns.Add("OBJECT",    Type.GetType("System.String"));
            result.AcceptChanges();

            //Initialize working variables
            Dictionary<String, String> constructRow = new Dictionary<String, String>();
            constructRow.Add("SUBJECT",   null);
            constructRow.Add("PREDICATE", null);
            constructRow.Add("OBJECT",    null);

            //Iterate on the templates
            foreach (RDFPattern tp in constructQuery.Templates.Where(tp => tp.Variables.Count == 0 || 
                                                                     tp.Variables.TrueForAll(v => resultTable.Columns.Contains(v.ToString())))) {

                #region GROUND TEMPLATE
                //Check if the template is ground, so represents an explicit triple to be added as-is
                if (tp.Variables.Count       == 0) {
                    constructRow["SUBJECT"]   = tp.Subject.ToString();
                    constructRow["PREDICATE"] = tp.Predicate.ToString();
                    constructRow["OBJECT"]    = tp.Object.ToString();
                    RDFQueryUtilities.AddRow(result, constructRow);
                    continue;
                }
                #endregion

                #region NON-GROUND TEMPLATE
                //Iterate the result datatable's rows to construct the triples of the current template
                IEnumerator rowsEnum              = resultTable.Rows.GetEnumerator();
                while (rowsEnum.MoveNext()) {

                    #region SUBJECT
                    //Subject of the template is a variable
                    if (tp.Subject is RDFVariable) {

                        //Check if the template must be skipped, in order to not produce illegal triples
                        //Row contains an unbound value in position of the variable corresponding to the template subject
                        if (((DataRow)rowsEnum.Current).IsNull(tp.Subject.ToString())) {
                            continue;
                        }

                        RDFPatternMember subj     = RDFQueryUtilities.ParseRDFPatternMember(((DataRow)rowsEnum.Current)[tp.Subject.ToString()].ToString());
                        //Row contains a literal in position of the variable corresponding to the template subject
                        if (subj is RDFLiteral) {
                            continue;
                        }
                        //Row contains a resource in position of the variable corresponding to the template subject
                        constructRow["SUBJECT"]   = subj.ToString();

                    }
                    //Subject of the template is a resource
                    else {
                        constructRow["SUBJECT"]   = tp.Subject.ToString();
                    }
                    #endregion

                    #region PREDICATE
                    //Predicate of the template is a variable
                    if (tp.Predicate is RDFVariable) {

                        //Check if the template must be skipped, in order to not produce illegal triples
                        //Row contains an unbound value in position of the variable corresponding to the template predicate
                        if (((DataRow)rowsEnum.Current).IsNull(tp.Predicate.ToString())) {
                            continue;
                        }
                        RDFPatternMember pred     = RDFQueryUtilities.ParseRDFPatternMember(((DataRow)rowsEnum.Current)[tp.Predicate.ToString()].ToString());
                        //Row contains a blank resource or a literal in position of the variable corresponding to the template predicate
                        if ((pred is RDFResource && ((RDFResource)pred).IsBlank) || pred is RDFLiteral) {
                            continue;
                        }
                        //Row contains a non-blank resource in position of the variable corresponding to the template predicate
                        constructRow["PREDICATE"] = pred.ToString();

                    }
                    //Predicate of the template is a resource
                    else {
                        constructRow["PREDICATE"] = tp.Predicate.ToString();
                    }
                    #endregion

                    #region OBJECT
                    //Object of the template is a variable
                    if (tp.Object is RDFVariable) {

                        //Check if the template must be skipped, in order to not produce illegal triples
                        //Row contains an unbound value in position of the variable corresponding to the template object
                        if (((DataRow)rowsEnum.Current).IsNull(tp.Object.ToString())) {
                            continue;
                        }
                        RDFPatternMember obj      = RDFQueryUtilities.ParseRDFPatternMember(((DataRow)rowsEnum.Current)[tp.Object.ToString()].ToString());
                        //Row contains a resource or a literal in position of the variable corresponding to the template object
                        constructRow["OBJECT"]    = obj.ToString();

                    }
                    //Object of the template is a resource or a literal
                    else {
                        constructRow["OBJECT"]    = tp.Object.ToString();
                    }
                    #endregion

                    //Insert the triple into the final table
                    RDFQueryUtilities.AddRow(result, constructRow);

                }
                #endregion

            }

            return result;
        }
        /// <summary>
        /// Get the result table of the given pattern group
        /// </summary>
        internal static void CombinePatterns(RDFConstructQuery query, RDFPatternGroup patternGroup) {
            if (patternGroup.Patterns.Any()) {

                //Populate pattern group result table
                DataTable patternGroupResultTable = RDFQueryEngine.CombineTables(query.PatternResultTables[patternGroup], false);

                //Add it to the list of pattern group result tables
                query.PatternGroupResultTables.Add(patternGroup, patternGroupResultTable);

                //Populate its metadata
                query.PatternGroupResultTables[patternGroup].TableName = patternGroup.ToString();
                if (!query.PatternGroupResultTables[patternGroup].ExtendedProperties.ContainsKey("IsOptional")) {
                    query.PatternGroupResultTables[patternGroup].ExtendedProperties.Add("IsOptional", patternGroup.IsOptional);
                }
                else {
                    query.PatternGroupResultTables[patternGroup].ExtendedProperties["IsOptional"]  = patternGroup.IsOptional;
                }
                if (!query.PatternGroupResultTables[patternGroup].ExtendedProperties.ContainsKey("JoinAsUnion")) {
                    query.PatternGroupResultTables[patternGroup].ExtendedProperties.Add("JoinAsUnion", patternGroup.JoinAsUnion);
                }
                else {
                    query.PatternGroupResultTables[patternGroup].ExtendedProperties["JoinAsUnion"] = patternGroup.JoinAsUnion;
                }

            }
        }