/////////////////////////////////////////////////////////// // SearchResultListRI.cs // Implementation of Class SearchResultListRI // CSLA type: Read only collection // Created on: 01-Oct-2014 // Object design: John // Coded: 01-Oct-2014 /////////////////////////////////////////////////////////// using System; using System.Data; using GZTW.Data; using CSLA.Data; using CSLA; using System.Text; using System.Collections.Generic; //using log4net; namespace GZTW.AyaNova.BLL { /// /// Read only collection of objects /// [Serializable] public class SearchResultListRI : ReadOnlyCollectionBase { #region Data structure /// /// /// [Serializable] public struct SearchResultListRIInfo { internal Guid mRootObjectID; internal RootObjectTypes mRootObjectType; internal Guid mAncestorRootObjectID; internal RootObjectTypes mAncestorRootObjectType; internal string mExtract; internal string mDescription; internal float mRank; internal GridNameValueCellItem mSource; //Public properties /// /// Object id most closely associated with search result (may not be a directly openable object, that's what ancestor style is for) /// [Display(DisplayType.Hidden)] public Guid RootObjectID { get { return mRootObjectID; } } /// /// Object type most closely associated with search result /// [Display(DisplayType.Hidden)] public RootObjectTypes RootObjectType { get { return mRootObjectType; } } /// /// Object to open to view this search results original record /// If this object is not a child or grandchild, then this is the same as RootObjectID /// [Display(DisplayType.Hidden)] public Guid AncestorRootObjectID { get { return mAncestorRootObjectID; } } /// /// Object type to open to view this search results original record /// [Display(DisplayType.Hidden)] public RootObjectTypes AncestorRootObjectType { get { return mAncestorRootObjectType; } } /// /// A brief excerpt automatically generated that tries to show the most relevant block of text /// in the search result for the given search string /// [Display(DisplayType.Text)] public string LT_SearchResult_Label_Extract { get { return mExtract; } } /// /// Description of search result /// [Display(DisplayType.Text)] public string LT_SearchResult_Label_Description { get { return mDescription; } } /// /// Ranking of search result in order of how closely it matches the searched for string of text /// See class for an explanation of this value /// [Display(DisplayType.DecimalNumber)] public float LT_SearchResult_Label_Rank { get { return mRank; } } internal SmartDate mCreated; /// /// Date and time object found that matches was created /// [Display(DisplayType.DateTime)] public object LT_Common_Label_Created { get { return mCreated.DBValue; } } internal SmartDate mModified; /// /// Date and time object found that matches was last modified /// [Display(DisplayType.DateTime)] public object LT_Common_Label_Modified { get { return mModified.DBValue; } } //Case 194 //internal Guid mCreator; //[Display(DisplayType.ListUsers)] //public Guid LT_Common_Label_Creator {get{return mCreator;}} //internal Guid mModifier; //[Display(DisplayType.ListUsers)] //public Guid LT_Common_Label_Modifier {get{return mModifier;}} internal string mCreator; /// /// Creator of matching found object record /// [Display(DisplayType.Text)] public string LT_Common_Label_Creator { get { return mCreator; } } internal string mModifier; /// /// Modifier of matching found object record /// [Display(DisplayType.Text)] public string LT_Common_Label_Modifier { get { return mModifier; } } /// /// Descriptive text and id of openable item to view source of search result /// (used to create search result list buttons in AyaNova UI) /// [Display(DisplayType.Button, RootObjectType = RootObjectTypes.SearchResult)] public GridNameValueCellItem LT_SearchResult_Label_Source { get { return mSource; } } /// /// /// /// public bool Equals(SearchResultListRIInfo obj) { return this.mRootObjectID.Equals(obj.mRootObjectID); } }//end SearchResultListRIInfo #endregion #region Constructor /// /// /// protected SearchResultListRI() { ////case 1039 //log.Debug("SearchResultListRI()"); // AllowSort=false; // AllowFind=true; // AllowEdit=false; // AllowNew=false; // AllowRemove=false; } #endregion #region Business properties and methods /// /// Get item by index /// /// public SearchResultListRIInfo this[int Item] { get { return (SearchResultListRIInfo)List[Item]; } } /// /// Returns SearchResultListRIInfo item that matches passed in itemid value /// /// public SearchResultListRIInfo this[Guid ItemID] { get { foreach (SearchResultListRIInfo child in List) { if (child.mRootObjectID == ItemID) return child; } throw new ArgumentException("SearchResultListRI: ID not found:\r\n" + ItemID.ToString()); } } #endregion #region contains /// /// Check if item in collection /// /// public bool Contains(SearchResultListRIInfo obj) { foreach (SearchResultListRIInfo child in List) { if (child.Equals(obj)) return true; } return false; } #endregion #region Static methods /// /// Responsive interface search list /// /// /// /// public static SearchResultListRI GetList(string Search, int MaxRecords = -1) { ////case 1039 //log.Debug("GetList("+Search+")"); //if empty search return empty list if (Search == null) return new SearchResultListRI(); if (Search == "") return new SearchResultListRI(); //scrub search string for sql injection attacks //and pass to fetch through criteria return (SearchResultListRI)DataPortal.Fetch(new Criteria(Search.Replace("\"", ""), MaxRecords)); } #endregion #region DAL DATA ACCESS /// /// protected override void DataPortal_Fetch(object Criteria) { Criteria crit = (Criteria)Criteria; if (crit.Search == "") return; crit.Search = crit.Search.Replace("*", "%"); string[] saTerms = AyaBizUtils.BreakSearchPhrase(crit.Search).Split(','); if (saTerms == null || saTerms.GetLength(0) == 0) return; //Case 194 UserPickList upl = null; upl = UserPickList.GetList(false); #region Numeric only search //If it's only digits optimize search for auto numbered items (workorder, quote, pm, PO, Inventory adjustment (but probably not that one)) if (AyaBizUtils.DigitsOnly(crit.Search)) { Guid serviceId = DBUtil.ToGuid(DBUtil.GetScalarFromSQLString( "SELECT AWORKORDER.AID " + "FROM AWORKORDER " + "INNER JOIN AWORKORDERSERVICE ON (AWORKORDER.AID = AWORKORDERSERVICE.AWORKORDERID) " + "WHERE AWORKORDERSERVICE.ASERVICENUMBER = " + crit.Search + "AND AWORKORDER.AWORKORDERTYPE = 1 " ));//"SELECT AWORKORDERID FROM AWORKORDERSERVICE WHERE ASERVICENUMBER = " + crit.Search Guid quoteId = DBUtil.ToGuid(DBUtil.GetScalarFromSQLString( "SELECT AWORKORDERQUOTE.AID " + "FROM AWORKORDER " + "INNER JOIN AWORKORDERQUOTE ON (AWORKORDER.AID = AWORKORDERQUOTE.AWORKORDERID) " + "WHERE AWORKORDERQUOTE.AQUOTENUMBER = " + crit.Search + "AND AWORKORDER.AWORKORDERTYPE = 3 " //"SELECT AWORKORDERID FROM AWORKORDERQUOTE WHERE AQUOTENUMBER = " + crit.Search )); Guid pmId = DBUtil.ToGuid(DBUtil.GetScalarFromSQLString( "SELECT AWORKORDERPREVENTIVEMAINTENANCE.AID " + "FROM AWORKORDER " + "INNER JOIN AWORKORDERPREVENTIVEMAINTENANCE ON (AWORKORDER.AID = AWORKORDERPREVENTIVEMAINTENANCE.AWORKORDERID) " + "WHERE AWORKORDERPREVENTIVEMAINTENANCE.APREVENTIVEMAINTENANCENUMBER = " + crit.Search + "AND AWORKORDER.AWORKORDERTYPE = 2 " // "SELECT AWORKORDERID FROM AWORKORDERPREVENTIVEMAINTENANCE WHERE APREVENTIVEMAINTENANCENUMBER = " + crit.Search )); Guid poId = DBUtil.ToGuid(DBUtil.GetScalarFromSQLString("SELECT AID FROM APURCHASEORDER WHERE APONUMBER = " + crit.Search)); AddNumericHit(RootObjectTypes.Workorder, serviceId, saTerms, upl); // AddNumericHit(RootObjectTypes.Workorder, quoteId, saTerms, upl); // AddNumericHit(RootObjectTypes.Workorder, pmId, saTerms, upl); AddNumericHit(RootObjectTypes.WorkorderQuote, quoteId, saTerms, upl); AddNumericHit(RootObjectTypes.WorkorderPreventiveMaintenance, pmId, saTerms, upl); AddNumericHit(RootObjectTypes.PurchaseOrder, poId, saTerms, upl); } #endregion numeric search #region natural search //sql ize it // crit.Search = crit.Search.Replace("*", "%"); StringBuilder sbSQL = new StringBuilder(); //convert search terms to query string q = "SELECT ~MAXRECS~ aSearchKey.aSourceObjectID, aSearchKey.aSourceObjectType " + "FROM aSearchDictionary INNER " + "JOIN aSearchKey ON aSearchDictionary.aID " + "= aSearchKey.aWordID WHERE "; if (crit.MaxRecords > 0) q = q.Replace("~MAXRECS~", "TOP " + crit.MaxRecords.ToString()); else q = q.Replace("~MAXRECS~", ""); sbSQL.Append(q); //case 3581 string insertNationalTypeQueryCharacter = string.Empty; if (DBUtil.DB.DBType == DataBaseType.MSSQL) { insertNationalTypeQueryCharacter = "N"; } foreach (string sTerm in saTerms) { if (sTerm.IndexOf("%") != -1) sbSQL.Append(" (aSearchDictionary.aWord Like " + insertNationalTypeQueryCharacter + "'" + sTerm + "') OR");//case 3581 else sbSQL.Append(" (aSearchDictionary.aWord = " + insertNationalTypeQueryCharacter + "'" + sTerm + "') OR");//case 3581 } //trim off the final "OR" sbSQL.Length = sbSQL.Length - 2; sbSQL.Append(" GROUP BY aSearchKey.aSourceObjectID, " + "aSearchKey.aSourceObjectType HAVING (COUNT(*) " + "= " + saTerms.GetLength(0).ToString() + ")"); //------------------ SafeDataReader dr = null; try { dr = DBUtil.GetReaderFromSQLString(sbSQL.ToString()); while (dr.Read()) { //standard search SearchResult sr = AyaBizUtils.GetSearchResultFor((RootObjectTypes)dr.GetInt16("aSourceObjectType"), dr.GetGuid("aSourceObjectID"), saTerms); //sr.rank will equal zero if there is no good match or //if the user has no rights to retrieve that object etc if (sr.Rank > 0) { sr = MarkTerms(sr, saTerms); SearchResultListRIInfo info = new SearchResultListRIInfo(); info.mRootObjectID = dr.GetGuid("aSourceObjectID"); info.mRootObjectType = (RootObjectTypes)dr.GetInt16("aSourceObjectType"); info.mAncestorRootObjectID = sr.AncestorRootObjectID; info.mAncestorRootObjectType = sr.AncestorRootObjectType; info.mDescription = sr.Description; info.mExtract = sr.Extract; info.mRank = sr.Rank; info.mModified = sr.Modified; info.mModifier = upl[sr.Modifier]; info.mCreated = sr.Created; info.mCreator = upl[sr.Creator]; info.mSource = new GridNameValueCellItem( info.mAncestorRootObjectID, EnumDescConverter.GetEnumDescription(info.mRootObjectType), info.mAncestorRootObjectType); InnerList.Add(info); } } } finally { if (dr != null) dr.Close(); } #endregion natural } private SearchResult MarkTerms(SearchResult sr, string [] terms) { foreach (string s in terms) { sr.Extract=sr.Extract.Replace(s, "" + s + ""); } return sr; } private void AddNumericHit(RootObjectTypes roType, Guid id, string[] term, UserPickList upl) { //bugbug: quote, pm not showing description nor extract. PO is only showing vendor name in extract. if (id == Guid.Empty) return; RootObjectTypes srType = roType; //it appears that search result doesn't work with specific subtypes of workorders only the workorder type itself //if (roType == RootObjectTypes.WorkorderQuote || roType == RootObjectTypes.WorkorderPreventiveMaintenance) // srType = RootObjectTypes.Workorder; SearchResult sr = AyaBizUtils.GetSearchResultFor(srType, id, term); //description should only be if the user has no rights or not in region if (null==sr.Description) return; sr = MarkTerms(sr, term); SearchResultListRIInfo info = new SearchResultListRIInfo(); info.mAncestorRootObjectID = info.mRootObjectID = id; info.mAncestorRootObjectType = info.mRootObjectType = roType; info.mDescription = sr.Description; info.mExtract = sr.Extract; // since this is a direct hit based on the number and the user typed in only the number it should rank super high info.mRank = 99; info.mModified = sr.Modified; info.mModifier = upl[sr.Modifier]; info.mCreated = sr.Created; info.mCreator = upl[sr.Creator]; info.mSource = new GridNameValueCellItem( info.mAncestorRootObjectID, EnumDescConverter.GetEnumDescription(info.mRootObjectType) + " - " + term[0], info.mAncestorRootObjectType); InnerList.Add(info); } #endregion #region criteria /// /// Criteria for identifying existing object /// [Serializable] private class Criteria { public string Search; public int MaxRecords; public Criteria(string _Search, int _MaxRecords) { Search = _Search; MaxRecords = _MaxRecords; } } #endregion }//end SearchResultListRI }//end namespace GZTW.AyaNova.BLL