I'm using Solr 7.5 with Sitecore 9.2. I have some complex code that generates a search query, which I'm now trying to apply boosting to to prioritize page items over media assets (the relevant lines begin at isAssetPredicate....
public override Expression<Func<T, bool>> Build()
{
string query = _values.FirstOrDefault();
string queryLower = query.ToLower();
if (string.IsNullOrEmpty(query)) return null;
var predicate = PredicateBuilder.False<T>();
var exactPredicate = PredicateBuilder.False<T>();
exactPredicate = exactPredicate.Or(x => x.ItemPartNumberLower.Contains(queryLower)).Boost(90);
exactPredicate = exactPredicate.Or(x => x.TitleLower.Contains(queryLower)).Boost(99);
exactPredicate = exactPredicate.Or(x => x.Keywords.Contains(queryLower)).Boost(9);
exactPredicate = exactPredicate.Or(x => x.FileType.Contains(queryLower)).Boost(9);
exactPredicate = exactPredicate.Or(x => x.AggregatedSearchFields.Contains(queryLower)).Boost(9);
var fuzzyPredicate = PredicateBuilder.False<T>();
fuzzyPredicate = fuzzyPredicate.Or(x => x.ItemPartNumberLower.Contains(queryLower)).Boost(90);
fuzzyPredicate = fuzzyPredicate.Or(x => x.TitleLower.Contains(queryLower)).Boost(99);
fuzzyPredicate = fuzzyPredicate.Or(x => x.Keywords.Contains(queryLower)).Boost(9);
fuzzyPredicate = fuzzyPredicate.Or(x => x.FileType.Contains(queryLower)).Boost(9);
fuzzyPredicate = fuzzyPredicate.Or(x => x.AggregatedSearchFields.Contains(queryLower)).Boost(9);
// try searching for each term separately rather than exact match
var queryParts = queryLower.Trim().Split(' ');
if (queryParts.Length > 1)
{
var partsPredicate = PredicateBuilder.True<T>();
var fuzzyPartsPredicate = PredicateBuilder.False<T>();
foreach (var part in queryParts)
{
if (String.IsNullOrEmpty(part)) continue;
var subQuery = PredicateBuilder.False<T>();
subQuery = subQuery.Or(x => x.TitleLower.Contains(part)).Boost(99);
subQuery = subQuery.Or(x => x.ItemPartNumberLower.Contains(part)).Boost(95);
subQuery = subQuery.Or(x => x.Keywords.Contains(part)).Boost(80);
subQuery = subQuery.Or(x => x.FileType.Contains(part)).Boost(1);
subQuery = subQuery.Or(x => x.AggregatedSearchFields.Contains(part)).Boost(1);
partsPredicate = partsPredicate.And(subQuery);
var subQueryFuzzy = PredicateBuilder.False<T>();
subQueryFuzzy = subQueryFuzzy.Or(x => x.TitleLower.Like(part)).Boost(99);
subQueryFuzzy = subQueryFuzzy.Or(x => x.ItemPartNumberLower.Like(part)).Boost(95);
subQueryFuzzy = subQueryFuzzy.Or(x => x.Keywords.Like(part)).Boost(80);
subQueryFuzzy = subQueryFuzzy.Or(x => x.FileType.Like(part)).Boost(1);
subQueryFuzzy = subQueryFuzzy.Or(x => x.AggregatedSearchFields.Like(part)).Boost(1);
fuzzyPartsPredicate = fuzzyPartsPredicate.Or(subQueryFuzzy);
}
exactPredicate = exactPredicate.Or(partsPredicate).Boost(1);
fuzzyPredicate = fuzzyPredicate.Or(fuzzyPartsPredicate).Boost(1);
}
var queryPredicate = PredicateBuilder.False<T>();
queryPredicate = queryPredicate.Or(exactPredicate).Boost(99);
queryPredicate = queryPredicate.Or(fuzzyPredicate).Boost(1);
predicate = predicate.Or(queryPredicate);
if (HasAttribute(FieldType.Title))
{
predicate = predicate.Or(GetQueryExpression(FieldType.Title, query));
}
if (HasAttribute(FieldType.Description))
{
predicate = predicate.Or(GetQueryExpression(FieldType.Description, query));
}
if (HasAttribute(FieldType.Body))
{
predicate = predicate.Or(GetQueryExpression(FieldType.Body, query));
}
else if (QueryFormatter.NeedsFormatting(query))
{
predicate = predicate.Or(x => x.Content.MatchWildcard(QueryFormatter.FormatQuery(query)));
}
else
{
predicate = predicate.Or(x => x.Content.Like(query, SiteSettings.MinimumSimilarity));
}
var isAssetPredicate = PredicateBuilder.True<T>().And(x => x.IsAsset.Boost(0.0000001f)).And(predicate).Boost(0.0000001f);
var isPagePredicate = PredicateBuilder.True<T>().And(x => !x.IsAsset.Boost(9999f)).And(predicate).Boost(9f);
var finalPredicate = PredicateBuilder.True<T>();
finalPredicate = finalPredicate.Or(isAssetPredicate.Boost(0.1f)).Boost(0.1f);
finalPredicate = finalPredicate.Or(isPagePredicate.Boost(9f)).Boost(9f);
return finalPredicate;
}
As you can see I've put boosting in multiple places (I've tried it both within the predicate and outside the predicate separately as well as together) but none of it is getting applied in the query:
?q=(((c_is_asset_b:(True)^0.01 AND (c_item_part_number_lower_s:(*systemkit*) OR c_title_lower_s:(*systemkit*) OR c_keywords_t:(*systemkit*) OR c_file_type_s:(*systemkit*) OR c_aggregated_search_fields_s:(*systemkit*) OR (c_item_part_number_lower_s:(*systemkit*) OR c_title_lower_s:(*systemkit*) OR c_keywords_t:(*systemkit*) OR c_file_type_s:(*systemkit*) OR c_aggregated_search_fields_s:(*systemkit*)) OR (title_t:(systemkit~100000))^2.5 OR (summary_t:(systemkit~100000))^1.5 OR _content:(systemkit~100000))) OR ((-c_is_asset_b:(True) *:*)^9999 AND (c_item_part_number_lower_s:(*systemkit*) OR c_title_lower_s:(*systemkit*) OR c_keywords_t:(*systemkit*) OR c_file_type_s:(*systemkit*) OR c_aggregated_search_fields_s:(*systemkit*) OR (c_item_part_number_lower_s:(*systemkit*) OR c_title_lower_s:(*systemkit*) OR c_keywords_t:(*systemkit*) OR c_file_type_s:(*systemkit*) OR c_aggregated_search_fields_s:(*systemkit*)) OR (title_t:(systemkit~100000))^2.5 OR (summary_t:(systemkit~100000))^1.5 OR _content:(systemkit~100000)))) AND _val_:("recip(ms(NOW, c_date_tdt), 3.16e-11, 100, 1.8)"))&start=0&rows=100&fq=((((-c_alltemplates_sm:(b906556e0ab94174952026c282933569) *:*) OR end_date_tdt:[2021-09-13T17:25:05.524Z TO *] OR start_date_tdt:[2021-09-13T17:25:05.524Z TO *]) AND c_searchable_b:(True)) AND _language:(en))&fq=_indexname:(sitecore_web_index)&facet=true&facet.field=c_locations_sm&f.c_locations_sm.facet.mincount=1&facet.field=c_architectures_sm&f.c_architectures_sm.facet.mincount=1&facet.field=c_topics_sm&f.c_topics_sm.facet.mincount=1&facet.field=c_content_type_s&f.c_content_type_s.facet.mincount=1&facet.field=c_file_type_s&f.c_file_type_s.facet.mincount=1&wt=xml
(there is some boosting in the Title and Summary, but not on the Is_Asset field that I'm trying to boost)
It turns out the issue was that boosting does not work on boolean fields. I changed the IsAsset field to a string ("true" or "false") and was then able to implement boosting
var isAssetPredicate = PredicateBuilder.True<T>().And(predicate).And(x => x.IsAssetStr.Equals("true").Boost(0.001f));
var isPagePredicate = PredicateBuilder.True<T>().And(predicate).And(x => x.IsAssetStr.Equals("false").Boost(999f));
Boosting SOLR queries does not work in sitecore versions 9.1-9.3. See below from sitecore website:
https://doc.sitecore.com/en/developers/93/platform-administration-and-architecture/search-result-boosting.html
Search result boosting works in Solr version 6.6 or earlier, but Solr changed the implementation in Solr 7, and later versions. Therefore, search result boosting is broken in Sitecore 9.1, 9.2, and 9.3.
Related
This question already has an answer here:
How to select top N rows for each group in a Entity Framework GroupBy with EF 3.1
(1 answer)
Closed 6 months ago.
This is my first time using an anonymous function with LINQ syntax. We are getting multiple Id's to set up a way for us to query another table for a specific "effective date" but something is throwing off my query.
Error Message
'((Microsoft.AspNetCore.Http.DefaultHttpContext)httpContext).Session' threw an exception of type 'System.InvalidOperationException'
"System.InvalidOperationException: The LINQ expression
'GroupByShaperExpression:\nKeySelector: new { \n operatorId =
t.OperatorId, \n regionId = t.RegionId\n },
\nElementSelector:new { \n operatorId =
ProjectionBindingExpression: operatorId, \n regionId =
ProjectionBindingExpression: regionId, \n effectiveDate =
ProjectionBindingExpression: effectiveDate\n }\n
.OrderByDescending(ed => ed.effectiveDate)' could not be
translated. Either rewrite the query in a form that can be
translated, or switch to client evaluation explicitly by
inserting
a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or
'ToListAsync'.
Any help would be great!
Query
var opRegionEffectiveDate = await (from reg in _orppr.GetAllQueryable()
.Where(opReg => result.Select(o => o.Id).Contains(opReg.OperatorId)).Select(opReg => new {operatorId = opReg.OperatorId, regionId = opReg.RegionId, effectiveDate = opReg.EffectiveDate}).Distinct()
group reg by new {reg.operatorId, reg.regionId}
into regef
select new { oprId = regef.Key.operatorId, regId = regef.Key.regionId, efdate = regef.OrderByDescending(ed => ed.effectiveDate).FirstOrDefault()})
.ToListAsync();
Full Method
public async Task<IEnumerable<OperatorViewModel>> GetAllAsync()
{
var retList = new List<OperatorViewModel>();
var result = await _operatorRepository.GetAllQueryable().Include(o => o.GroupEmails).Where(o => o.TenantId == TenantId).ToListAsync();
// var regions = _orppr.GetAllQueryable();
var opRegionEffectiveDate = await (from reg in _orppr.GetAllQueryable()
.Where(opReg => result.Select(o => o.Id).Contains(opReg.OperatorId)).Select(opReg => new {operatorId = opReg.OperatorId, regionId = opReg.RegionId, effectiveDate = opReg.EffectiveDate}).Distinct()
group reg by new {reg.operatorId, reg.regionId}
into regef
select new { oprId = regef.Key.operatorId, regId = regef.Key.regionId, efdate = regef.OrderByDescending(ed => ed.effectiveDate).FirstOrDefault()})
.ToListAsync();
var regions = await _regionRepository.GetAllQueryable()
.Where(reg => opRegionEffectiveDate.Select(ore => ore.regId).Contains(reg.Id))
.ToListAsync();
foreach (var oper in result)
{
var regionsToAdd = opRegionEffectiveDate.Where(r => r.oprId == oper.Id).Select(r => r.regId);
var regionsList = regions.Where(r => regionsToAdd.Contains(r.Id));
// var currRegions = await regions.Where(opReg => opReg.OperatorId == oper.Id)
// .Include(opReg => opReg.Region)
// .Select(opReg => opReg.Region)
// .OrderBy(reg => reg.Name)
// .Distinct()
// .ToListAsync();
var currOper = _mapper.Map<Operator, OperatorViewModel>(oper);
currOper.Regions = _mapper.Map<IEnumerable<Region>, IEnumerable<RegionViewModel>>(regionsList);
foreach(var reg in currOper.Regions)
{
var item = opRegionEffectiveDate.Single(r => r.oprId == oper.Id && r.regId == reg.Id);
reg.EffectiveDate = item.efdate.ToString();
}
retList.Add(currOper);
}
return retList.OrderBy(r => r.OperatorName);
}
Try following :
var opRegionEffectiveDate = await (_orppr
.GetAllQueryable().Where(opReg => result.Select(o => o.Id).Contains(opReg.OperatorId))
.OrderByDescending(ed => ed.effectiveDate)
.Select(opReg => new {operatorId = opReg.OperatorId, regionId = opReg.RegionId, effectiveDate = opReg.EffectiveDate})
.GroupBy(reg =>new {reg.operatorId, reg.regionId})
.Select(x => x).FirstOrDefault()
.Select(x => new { oprId = x.Key.operatorId, regId = x.Key.regionId, x.effectiveDate})
.ToListAsync()
);
var opRegionEffectiveDate = await _orppr
.GetAllQueryable().Where(opReg => result.Select(o => o.Id).Contains(opReg.OperatorId))
.Distinct()
.OrderByDescending(ed => ed.effectiveDate)
.Select(opReg => new {operatorId = opReg.OperatorId, regionId = opReg.RegionId, effectiveDate = opReg.EffectiveDate})
.GroupBy(reg =>new {reg.operatorId, reg.regionId})
.Select(x => x).FirstOrDefault()
.Select(x => new { oprId = x.operatorId, regId = x.regionId, x.effectiveDate})
.ToListAsync();
I try to compose a dynamic query with NEST (Elastichsearc library for dotnet 5.0) but only the first code work:
Case 1: (Work)
var response1 = await client.SearchAsync<VideoManifestElasticDto>(s =>
s.Query(q => q
.Bool(b => b
.Must(mu => mu
.Wildcard(f => f.Title, '*' + dtoSearch.Title + '*')
))));
var aaa1 = response1.Documents;
Return 4 documents. It's OK
Case 2: (Not Work)
var response2 = await client.SearchAsync<VideoManifestElasticDto>(s =>
s.Query(q => q
.Bool(b => b
.Must(mu => new WildcardQuery() { Field = nameof(VideoManifestElasticDto.Title), CaseInsensitive = true, Value = '*' + dtoSearch.Title + '*' }
))));
var aaa2 = response2.Documents;
Return 0 documents. Why?
Case 3: (Not Work)
Last case, this is my goal i wan't create a dynamic query
var response3 = await client.SearchAsync<VideoManifestElasticDto>(Blah(dtoSearch));
var aaa3 = response3.Documents;
public static SearchDescriptor<VideoManifestElasticDto> Blah(VideoManifestElasticDto videoManifestElasticDto)
{
return new SearchDescriptor<VideoManifestElasticDto>().Query(b => b.Bool( c => c.Must(Orso(videoManifestElasticDto))));
}
public static QueryContainer[] Orso(VideoManifestElasticDto videoManifestElasticDto)
{
List<QueryContainer> queryContainerList = new List<QueryContainer>();
if (videoManifestElasticDto == null)
{
return queryContainerList.ToArray();
}
if (!string.IsNullOrWhiteSpace(videoManifestElasticDto.Title))
{
var orQuery = new WildcardQuery() { Field = nameof(VideoManifestElasticDto.Title), CaseInsensitive = true, Value = '*' + videoManifestElasticDto.Title + '*' };
queryContainerList.Add(orQuery);
}
else if (!string.IsNullOrWhiteSpace(videoManifestElasticDto.Description))
{
var orQuery = new MatchQuery() { Field = "Description", Query = videoManifestElasticDto.Description };
queryContainerList.Add(orQuery);
}
else if (!string.IsNullOrWhiteSpace(videoManifestElasticDto.VideoId))
{
var orQuery = new MatchQuery() { Field = "VideoId", Query = videoManifestElasticDto.VideoId };
queryContainerList.Add(orQuery);
}
return queryContainerList.ToArray();
}
Good Morning,
I have a problem with the performance if i use include.
Have a look at the following query, if I load the data from the include (x.Server_Item.itemType) the query needs like 980ms.
If i remove them, the query needs 51ms.
980ms:
using (var db = new dbContext())
{
var items = db.Characters_Inventory
.Where(x => x.charId == charId && x.itemLocation == ItemLocationInventory)
.Include(p => p.Server_Items)
.AsSplitQuery()
.Select(x => new
{
itemId = x.id,
x.itemName,
x.nameTag,
x.itemAmount,
x.itemLocation,
x.isItemEquipped,
x.itemCategory,
x.metaId,
x.metaTag0,
x.metaTag1,
itemType = x.Server_Items.itemType,
itemSubType = x.Server_Items.itemSubType,
itemClothesType = x.Server_Items.Server_Clothes.type,
itemPicName = x.Server_Items.itemPicSRC,
itemWeight = x.Server_Items.itemWeight,
itemGender = x.Server_Items.Server_Clothes.gender,
isItemUseable = x.Server_Items.isItemUseable,
isItemDroppable = x.Server_Items.isItemDroppable,
isItemGiveable = x.Server_Items.isItemGiveable,
isItemStorable = x.Server_Items.isItemStorable,
isItemStackable = x.Server_Items.isItemStackable,
isItemShowable = x.Server_Items.isItemShowable,
isItemNameable = x.Server_Items.isItemNameable,
isItemPlaceable = x.Server_Items.isItemPlaceable,
//isClothesTypeEquipped = IsClothesTypeEquipped(charId, x.id),
isClothesSwitchable = x.Server_Items.Server_Clothes.altClothesName != "" ? true : false
//isWeaponTypeEquipped = IsWeaponTypeEquipped(charId, x.id),
//charName = Characters.GetCharacterName(x.metaId)
}).AsNoTracking().ToList();
return JsonConvert.SerializeObject(items);
}
50ms:
using (var db = new dbContext())
{
var items = db.Characters_Inventory
.Where(x => x.charId == charId && x.itemLocation == ItemLocationInventory)
.Include(p => p.Server_Items)
.AsSplitQuery()
.Select(x => new
{
itemId = x.id,
x.itemName,
x.nameTag,
x.itemAmount,
x.itemLocation,
x.isItemEquipped,
x.itemCategory,
x.metaId,
x.metaTag0,
x.metaTag1
}).AsNoTracking().ToList();
return JsonConvert.SerializeObject(items);
}
I tried with AsSplitQuery and without AsSplitQuery.
Maybe nice to know: On the included table (Server_Items) is alrdy a table included.
Thanks in advance.
Sincerely
I want list of all unique Scheme_Codes but I am unable to write query. I tried this one but I am confused what's problem with this query.
var queryresult = db.MFData.GroupBy(x => new { Scheme_Name = x.Scheme_Name, Scheme_Code = x.Scheme_Code, FundFamily = x.FundFamily, Date = x.Date })
.Select(group => new
{
Scheme_name = group.Key.Scheme_Name,
Scheme_Code = group.Key.Scheme_Code,
FundFamily = group.Key.FundFamily,
Date = group.Max(x => x.Date),
count = group.Select( x => x.Scheme_Code).Distinct().Count()
}
).OrderBy(x => x.Scheme_Code);
I have this query but I am not sure how to convert this to linq
SELECT [Scheme_Code],[FundFamily],[Scheme_Name],
MAX([Date]) as LastDate
FROM [MFD].[dbo].[MFDatas]
GROUP BY [Scheme_Code],[Scheme_Name], [FundFamily]
ORDER BY [Scheme_Code]
All you have to do is omit the date from your groupby-clause:
var queryresult = db.MFData.GroupBy(x => new
{
Scheme_Name = x.Scheme_Name,
Scheme_Code = x.Scheme_Code,
FundFamily = x.FundFamily
}).Select(group => new
{
Scheme_name = group.Key.Scheme_Name,
Scheme_Code = group.Key.Scheme_Code,
FundFamily = group.Key.FundFamily,
Date = group.Max(x => x.Date),
count = group.Select(x => x.Scheme_Code).Distinct().Count()
}).OrderBy(x => x.Scheme_Code);
Let me change the question like this:
Which one of the below modes would increase the performance of an application?
A method in C# which performs about 6 JOIN statements and returns a value.
An stored procedure which performs 6 JOIN statements
Which of the above would be more efficient in terms of application performance?
Here is the Code:
public bool UserHasAccess(string userName, string actionName)
{
var decodedUserName = UtilityHands.GeneralTools.DecodeString(userName);
using (MAHAL_E_MA_Repository.UnitOfWork unit = new UnitOfWork())
{
var theUserID = unit.UserRepository.Get(filter: s => s.UserName.Equals(decodedUserName)).First().ID;
var userInRoleGroup = unit.RoleGroupUserRepository.Get(filter: r => r.UserInRole == theUserID).ToList();
var roleInRoleGroups = unit.RoleOfRoleGroupRepository.Get().ToList();
var serviceOfRoles = unit.ServiceOfRoleRepository.Get().ToList();
var serviceOfAction = unit.ServiceOfActionRepository.Get().ToList();
var roles = unit.RoleRepository.Get().ToList();
var service = unit.ServiceRepository.Get().ToList();
var actions = unit.ActionProviderRepository.Get().ToList();
var rolesOfRoleGroupJoin = userInRoleGroup.Join(roleInRoleGroups,
(u => u.RoleGroupID),
(r => r.RoleGroupID),
((u, r) => new { userInRoleGroup = u, roleInRoleGroups = r }));
var rrgOfRolesJoin = rolesOfRoleGroupJoin.Join(roles,
(r => r.roleInRoleGroups.RoleID),
(rs => rs.RoleID),
((r, rs) => new { rolesOfRoleGroupJoin = r, roles = rs }));
var roleServiceJoin = rrgOfRolesJoin.Join(serviceOfRoles,
(rrg => rrg.roles.RoleID),
(sor => sor.RoleID),
((rrg, sor) => new { rrgOfRolesJoin = rrg, serviceOfRoles = sor }));
var serviceOfRolesServiceJoin = roleServiceJoin.Join(service,
(rs => rs.serviceOfRoles.ServiceID),
(s => s.ServiceID),
((rs, s) => new { roleServiceJoin = rs, service = s }));
var serviceActionJoin = serviceOfRolesServiceJoin.Join(serviceOfAction,
(sors => sors.service.ServiceID),
(soa => soa.ServiceID),
((sors, soa) => new { serviceOfRolesServiceJoin = sors, serviceOfAction = soa }));
var serviceActionWithActionJoin = serviceActionJoin.Join(actions,
(sa => sa.serviceOfAction.ActionProviderID),
(a => a.ActionProviderID),
((sa, a) => new { serviceActionJoin = sa, actions = a }));
var actionNames = serviceActionWithActionJoin.Where(s => s.actions.Description.Equals(actionName));
if (actionNames != null && actionNames.Count() > 0)
{
return true;
}
return false;
}
}
Thank you
I assume that with "`A method in C# which performs about 6 JOIN statements and returns a value." you are doing it in Linq-to-SQL or Linq-to-Entities that translates all the joins to one query that hits the database.
In that case, there will be no noticable differences. The query execution plan is cached in the same way for both SPs and dynamically generated queries (SPs used to have better performance, but that difference is removed since several years).
If you are doing the join "in memory" by retrieving separate results from the DB you will get much, much worse performance.