Vidyano has the ability to set any Query as Global Search which allows the end users to use a simple text search in the menu that will search through all the queries and shows those queries that returned results (or errors). This has given us an easy way to provide this kind of functionality to users.
The only problem has always been performance, the application will launch a full text search for all columns on all the queries that are defined as global search so the total time to give results has always been in the 1-10 seconds range.
Instant search has been written from the ground up with performance as the number one priority.
Example
Instant search is implemented as an advanced feature, it is up to the developer to keep the total processing time as low as possible (recommended <100ms).
public sealed class MyCRMAdvanced : Advanced
{
public MyCRMAdvanced()
{
InstantSearchDelay = TimeSpan.FromMilliseconds(200d);
}
public override TimeSpan? GetInstantSearchDelayForUser(IUser user)
{
if (user.IsMemberOf("CantUseInstantSearch"))
return null;
// Fallback to default
return base.GetInstantSearchDelayForUser(user);
}
public override void HandleInstantSearch(InstantSearchArgs args)
{
args.MaxResults = 12; // Defaults to 15 results
var user = Manager.Current.User;
if (user.IsMemberOf("Administrators"))
{
// Some built-in searches specific for application admins
args.AddPersistentObjects();
args.AddUsers();
}
using (var context = new MyCRMEntityModelContainer())
{
var search = args.Search; // The search query from the end user
// The first parameter specifies the PO type name
args.AddRange("Customer", context.Customers
.Where(it => it.FirstName.Contains(search) || it.LastName.Contains(search))
.Select(it => new
{
ID = it.CustomerID,
it.FirstName,
it.LastName
})
.Take(4)
.AsEnumerable()
.Select(it => new InstantResult(it.ID.ToServiceString(), it.FirstName + " " + it.LastName)));
// The instant result determines the ObjectId and Breadcrumb to display the result in the menu
// For single property lookups we have an easy helper method
args.AddRange("Product", context.Products, it => it.ProductID, it => it.Name, 4); // 4 max results instead of the default 5
args.AddRange("ProductCategory", context.ProductCategories, it => it.ProductCategoryID, it => it.Name, 4);
int number;
if (int.TryParse(search, out number)) // We can skip certain tables if the search input doesn't match the expected input
{
args.AddRange("SalesOrderHeader", context.SalesOrderHeaders
.Where(it => it.SalesOrderNumber.Contains(search))
.Select(it => new
{
ID = it.SalesOrderID,
it.SalesOrderNumber
})
.Take(4)
.AsEnumerable()
.Select(it => new InstantResult(it.ID.ToServiceString(), it.SalesOrderNumber, it.SalesOrderNumber.IndexOf(search, StringComparison.OrdinalIgnoreCase) - 2)));
// The SalesOrderNumber column is a computed column ("SO" + ID) so we decrement the score by 2 for better ranking results
}
}
}
}