Click here to Skip to main content
15,896,207 members
Articles / Web Development / ASP.NET

Very Powerful, Generic, Irony Based, Database Searcher

Rate me:
Please Sign up or sign in to vote.
4.00/5 (6 votes)
26 Aug 2010Ms-PL8 min read 30.4K   546   29  
Usage of Irony to produce a Google-like search tool on any column in a database
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using log4net;
using searchComponent.structure;
using searchComponent.grammar;
using Irony.Parsing;

namespace searchComponent.interpreter
{
    /// <summary>
    /// This class is responsible for converting the search query string into a valid
    /// sql query string.
    /// </summary>
    public class SqlServerInterpreter
    {
        private static readonly ILog log = LogManager.GetLogger("Standard");

        #region Fields
        /// <summary>
        /// This is the structure base for which the interpreter will construct the sql query.
        /// </summary>
        private SearchStructure _structure;
        #endregion

        #region Constructor
        /// <summary>
        /// Constructor, only needs the structure.
        /// </summary>
        public SqlServerInterpreter(SearchStructure objStructure)
        {
            this._structure = objStructure;
        }
        #endregion


        #region Public Methods
        /// <summary>
        /// This method gets the query for SQLServer
        /// </summary>
        /// <param name="objColumn"></param>
        /// <param name="numericQuery"></param>
        /// <returns></returns>
        public string GetQuery(string aQuery)
        {
            FieldGrammar grammar = new FieldGrammar(_structure);
            LanguageData language = new LanguageData(grammar);
            Parser parser = new Parser(language);
            ParseTree parseTree = parser.Parse(aQuery);
            if (parseTree.Status == ParseTreeStatus.Error)
            {
                string messages = "";
                ParserMessageList listMessages = parseTree.ParserMessages;
                foreach(ParserMessage objMsg in listMessages) {
                    messages += objMsg.Message + "\n";
                }
                throw new Exception(messages);
            }
            ParseTreeNode root = parseTree.Root;

            StringBuilder query = new StringBuilder();
            query.Append(GetColumnsAndFrom());
            query.Append(" WHERE ");
            query.Append(ConvertQuery(root));
            return query.ToString();
        }

        /// <summary>
        /// Returns a list of possible suggestions for the query given accoding to the 
        /// structure contained in this object.
        /// It parses the query and searches for the Expected Terms value.
        /// Then, depending on the column calls the method GetSuggestion for the 
        /// corresponding grammar.
        /// </summary>
        /// <param name="query"></param>
        /// <returns></returns>
        public List<SuggestLine> GetSuggestion(string aQuery)
        {
            FieldGrammar grammar = new FieldGrammar(_structure);
            LanguageData language = new LanguageData(grammar);
            Parser parser = new Parser(language);
            ParseTree parseTree = parser.Parse(aQuery);

            StringSet expectedTerms = parser.Context.GetExpectedTermSet();

            return new List<SuggestLine>();
        }
        #endregion

        #region Private Methods
        /// <summary>
        /// This method obtains the select col1, col2, etc from tbl1 left|right|inner join tblx on 
        /// part of the sql query.
        /// </summary>
        /// <returns></returns>
        private string GetColumnsAndFrom()
        {
            StringBuilder result = new StringBuilder("SELECT ");

            foreach (TableStructure tbl in _structure.Tables)
            {
                foreach (Column col in tbl.Columns)
                {
                    if (col.AppearInResults)
                    {
                        result.Append(col.FullName + " AS " + col.Title + ", ");
                    }
                }
            }
            result.Remove(result.Length - 2, 2);
            result.Append(" FROM " + _structure.Tables[0].Name);

            for (int i = 1; i < _structure.Tables.Count; i++ )
            {
                TableStructure tbl = _structure.Tables[i];
                result.Append(tbl.GetJoinQuery());
            }

            return result.ToString();
        }

        /// <summary>
        /// Returns the string containing the sql Query corresponding to this ParseTreenode
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        private string ConvertQuery(ParseTreeNode node)
        {
            string result = "";
            // Note that some NonTerminals don't actually get into the AST tree, 
            // because of some Irony's optimizations - punctuation stripping and 
            // node bubbling. For example, ParenthesizedExpression - parentheses 
            // symbols get stripped off as punctuation, and child expression node 
            // (parenthesized content) replaces the parent ParExpr node (the 
            // child is "bubbled up").
            switch (node.Term.Name)
            {
                case "Expression":
                    if (node.ChildNodes.Count == 1)
                    {
                        result += ConvertQuery(node.ChildNodes[0]);
                    }
                    else
                    {
                        result += "( " + ConvertQuery(node.ChildNodes[0]) + " )";
                        result += " " + GetOperator(node.ChildNodes[1].ChildNodes[0].ChildNodes[0].Token.Text) + " ";
                        result += ConvertQuery(node.ChildNodes[2]) + "\n";
                    }
                    break;

                case "ColumnSearch":
                    Column objColumn = this._structure.GetColumnBySearchKey(node.ChildNodes[0].Token.Text);
                    ColumnInterpreter objInterpreter = ColumnInterpreter.GetColumnInterpreter(objColumn);
                    result += objInterpreter.GetWhereQuery(node.ChildNodes[2].Token.Text);
                    break;

                // This should never happen, even if input string is garbage
                default:
                    result += ". Please investigate.";
                    break;
            }
            return result;
        }

        /// <summary>
        /// Returns the correspondent operator in sql
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        private string GetOperator(string p)
        {
            if (p.Equals("|")) return "OR";
            if (p.Equals("&")) return "AND";

            log.Warn("Could not find operator " + p);

            return p;
        }
        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)


Written By
Chief Technology Officer Artexacta
Bolivia Bolivia
just a coder. Love Java, but working on asp.net

Comments and Discussions