476 lines
18 KiB
C#
476 lines
18 KiB
C#
///////////////////////////////////////////////////////////
|
|
// 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
|
|
{
|
|
/// <summary>
|
|
/// Read only collection of <see cref="SearchResultListRI.SearchResultListRIInfo"/> objects
|
|
/// </summary>
|
|
[Serializable]
|
|
public class SearchResultListRI : ReadOnlyCollectionBase
|
|
{
|
|
|
|
#region Data structure
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
[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
|
|
/// <summary>
|
|
/// Object id most closely associated with search result (may not be a directly openable object, that's what ancestor style is for)
|
|
/// </summary>
|
|
[Display(DisplayType.Hidden)]
|
|
public Guid RootObjectID { get { return mRootObjectID; } }
|
|
|
|
/// <summary>
|
|
/// Object type most closely associated with search result
|
|
/// </summary>
|
|
[Display(DisplayType.Hidden)]
|
|
public RootObjectTypes RootObjectType { get { return mRootObjectType; } }
|
|
|
|
/// <summary>
|
|
/// 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
|
|
/// </summary>
|
|
[Display(DisplayType.Hidden)]
|
|
public Guid AncestorRootObjectID { get { return mAncestorRootObjectID; } }
|
|
|
|
/// <summary>
|
|
/// Object type to open to view this search results original record
|
|
/// </summary>
|
|
[Display(DisplayType.Hidden)]
|
|
public RootObjectTypes AncestorRootObjectType { get { return mAncestorRootObjectType; } }
|
|
|
|
/// <summary>
|
|
/// A brief excerpt automatically generated that tries to show the most relevant block of text
|
|
/// in the search result for the given search string
|
|
/// </summary>
|
|
[Display(DisplayType.Text)]
|
|
public string LT_SearchResult_Label_Extract { get { return mExtract; } }
|
|
|
|
/// <summary>
|
|
/// Description of search result
|
|
/// </summary>
|
|
[Display(DisplayType.Text)]
|
|
public string LT_SearchResult_Label_Description { get { return mDescription; } }
|
|
|
|
/// <summary>
|
|
/// Ranking of search result in order of how closely it matches the searched for string of text
|
|
/// See <see cref="ExtractAndRank"/> class for an explanation of this value
|
|
/// </summary>
|
|
[Display(DisplayType.DecimalNumber)]
|
|
public float LT_SearchResult_Label_Rank { get { return mRank; } }
|
|
|
|
|
|
internal SmartDate mCreated;
|
|
/// <summary>
|
|
/// Date and time object found that matches was created
|
|
/// </summary>
|
|
[Display(DisplayType.DateTime)]
|
|
public object LT_Common_Label_Created { get { return mCreated.DBValue; } }
|
|
|
|
|
|
internal SmartDate mModified;
|
|
/// <summary>
|
|
/// Date and time object found that matches was last modified
|
|
/// </summary>
|
|
[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;
|
|
/// <summary>
|
|
/// Creator of matching found object record
|
|
/// </summary>
|
|
[Display(DisplayType.Text)]
|
|
public string LT_Common_Label_Creator { get { return mCreator; } }
|
|
|
|
internal string mModifier;
|
|
/// <summary>
|
|
/// Modifier of matching found object record
|
|
/// </summary>
|
|
[Display(DisplayType.Text)]
|
|
public string LT_Common_Label_Modifier { get { return mModifier; } }
|
|
|
|
|
|
/// <summary>
|
|
/// Descriptive text and id of openable item to view source of search result
|
|
/// (used to create search result list buttons in AyaNova UI)
|
|
/// </summary>
|
|
[Display(DisplayType.Button, RootObjectType = RootObjectTypes.SearchResult)]
|
|
public GridNameValueCellItem LT_SearchResult_Label_Source
|
|
{
|
|
get
|
|
{
|
|
return mSource;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="obj"></param>
|
|
public bool Equals(SearchResultListRIInfo obj)
|
|
{
|
|
return this.mRootObjectID.Equals(obj.mRootObjectID);
|
|
}
|
|
|
|
}//end SearchResultListRIInfo
|
|
#endregion
|
|
|
|
#region Constructor
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
protected SearchResultListRI()
|
|
{
|
|
|
|
////case 1039 //log.Debug("SearchResultListRI()");
|
|
// AllowSort=false;
|
|
// AllowFind=true;
|
|
// AllowEdit=false;
|
|
// AllowNew=false;
|
|
// AllowRemove=false;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Business properties and methods
|
|
|
|
/// <summary>
|
|
/// Get item by index
|
|
/// </summary>
|
|
/// <param name="Item"></param>
|
|
public SearchResultListRIInfo this[int Item]
|
|
{
|
|
|
|
get
|
|
{
|
|
return (SearchResultListRIInfo)List[Item];
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Returns SearchResultListRIInfo item that matches passed in itemid value
|
|
/// </summary>
|
|
/// <param name="ItemID"></param>
|
|
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
|
|
/// <summary>
|
|
/// Check if item in collection
|
|
/// </summary>
|
|
/// <param name="obj"></param>
|
|
public bool Contains(SearchResultListRIInfo obj)
|
|
{
|
|
foreach (SearchResultListRIInfo child in List)
|
|
{
|
|
if (child.Equals(obj)) return true;
|
|
}
|
|
return false;
|
|
|
|
}
|
|
#endregion
|
|
|
|
#region Static methods
|
|
|
|
/// <summary>
|
|
/// Responsive interface search list
|
|
/// </summary>
|
|
/// <param name="Search"></param>
|
|
/// <param name="MaxRecords"></param>
|
|
/// <returns></returns>
|
|
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
|
|
///
|
|
/// <param name="Criteria"></param>
|
|
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, "<mark>" + s + "</mark>");
|
|
}
|
|
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
|
|
/// <summary>
|
|
/// Criteria for identifying existing object
|
|
/// </summary>
|
|
[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 |