I am using Sitecore Solr search for searching using a keyword string, Is there a way to know the number of matches for each of the returned result items?.
The following is the code I am using:
using (var context = Index.CreateSearchContext())
{
List<Item> ResultList = new List<Item>();
var contentPredicate = PredicateBuilder.True<customSearchResultItem>();
contentPredicate = contentPredicate.And(p => p.Content.Contains(SearchKey));
contentPredicate = contentPredicate.And(p => p.Name != "__Standard Values");
var languagePredicate = PredicateBuilder.True<customSearchResultItem>();
languagePredicate = languagePredicate.And(p => p.Language == Context.Language.Name);
var CombinPredicates = PredicateBuilder.True<customSearchResultItem>();
CombinPredicates = CombinPredicates.And(languagePredicate);
CombinPredicates = CombinPredicates.And(contentPredicate);
// execute the search
IQueryable<customSearchResultItem> query = context.GetQueryable<customSearchResultItem>().Where(CombinPredicates);
var hits = query.GetResults().Hits;
}
From what I know, you can not get the number of matches for every result item based on the keyword used for search. What you can get, is a score value from Solr.
var hits = query.GetResults().Hits;
foreach (var hit in hits)
{
var score = hit.Score;
}
This is the value for the whole query, so it includes all predicates like language, not Standard Values and keywords in your case.
Remember, that this value can be different if you use Solr and if you use Lucene - this is dependent on the internal calculations.
I solved this by adding boosting values to each predicate then the for each result item I got the score and divide it by .59 which in my case the maximum value that occurs when all predicates staesfied; The code in details can be found on the following blog post:
http://sitecoreinfo.blogspot.com/2015/10/sitecore-solr-search-result-items.html
Related
I'm trying to find a way in Apps Script, using an array method (NOT using a table), to filter records from a large array that have text data in selected fields.
The records in the newly filtered array would contain ONLY those that have any (text) data in the specified field(s).
The filtered data array would then be copied into a 'Sheets' table for further use.
Finding records with blank fields using "" as the criteria, or specific data, such as "Yes", works well.
My work-around involves a 'for' loop to clear records from the table into which the array has been copied. However, this takes more time than than the 'filter' records method.
The example shows the method to filter only records that have a 'Blank' cell in the specified field. I have tried so many possible criteria that I've lost track of every option I have tried, but here are some of the criteria I've tried to find records that have non-blank cells: "<>" !="" !"" "!Null" ">0" "is not null" "!Empty".
var Ss = SpreadsheetApp.getActiveSpreadsheet();
var DataSheet = Ss.getSheetByName("VolunteerListTbl");//Source Table of data
var LstRowNum = DataSheet.getLastRow();
var LstColNum = DataSheet.getLastColumn();
// "DataSheetRangeValues" is an array of entire dataset
var DataSheetRangeValues = DataSheet.getRange(3,1, LstRowNum , LstColNum).getValues();
var FilterCriteria = ""; //CASE: NO Coordinator assigned
var FilteredData = DataSheetRangeValues.filter(function(e){return e[1]===FilterCriteria});
var NewSheetName = "CustomSearch_Tbl"; //Each search gets a different 'Sheet' name
var C_SrchResSheet = Ss.getSheetByName(NewSheetName);
//Copy data from 'FilteredData' array to a new table 'NewSheetName'
C_SrchResSheet.getRange(3,1,FilteredData.length,LstColNum).setValues(FilteredData);
RecordsFound = FilteredData.length;
Try this:
function myfunk() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("VolunteerListTbl");
const lr = sh.getLastRow();
const lc = sh.getLastColumn();
const vs = sh.getRange(3, 1, lr - 2, lc).getValues();//note the numrows param
const fvs = vs.filter(e => e[1] != "");//this does not seem consistent with the description in your question
const nsh = ss.getSheetByName("CustomSearch_Tbl");
nsh.getRange(3, 1, fvs.length, fvs[0].length).setValues(fvs);
}
The dapper tutorial gives this example to help a user with Multi Mapping (One to Many)
While this works I am curious why they have you store the orders in the dictionary but then in the end they use a linq.Distinct() and return from the list. It seems like it would be cleaner to just return the ordersDictionary.Values as the dictionary logic ensures no duplicates.
//Tutorial
using (var connection = new SqlConnection(FiddleHelper.GetConnectionStringSqlServerW3Schools()))
{
Dictionary<int,Order> orderDictionary = new Dictionary<int, Order>();
List<Order> list = connection.Query<Order, OrderDetail, Order>(sql, (order, orderDetail) =>
{
if (!orderDictionary.TryGetValue(order.OrderID, out Order orderEntry))
{
orderEntry = order;
orderEntry.OrderDetails = new List<OrderDetail>();
orderDictionary.Add(orderEntry.OrderID, orderEntry);
}
orderEntry.OrderDetails.Add(orderDetail);
return orderEntry;
}, splitOn: "OrderID")
.Distinct()
.ToList();
return list;
}
//my suggestion
using (var connection = new SqlConnection(FiddleHelper.GetConnectionStringSqlServerW3Schools()))
{
Dictionary<int,Order> orderDictionary = new Dictionary<int, Order>();
//change 1 no need to store into list here
connection.Query<Order, OrderDetail, Order>(sql, (order, orderDetail) =>
{
if (!orderDictionary.TryGetValue(order.OrderID, out Order orderEntry))
{
orderEntry = order;
orderEntry.OrderDetails = new List<OrderDetail>();
orderDictionary.Add(orderEntry.OrderID, orderEntry);
}
orderEntry.OrderDetails.Add(orderDetail);
return orderEntry;
}, splitOn: "OrderID"); //change 2 remove .Distinct().ToList()
return orderDictionary.Values.ToList(); //change 3 return dictionaryValues
}
I'm the author of this tutorial: https://dapper-tutorial.net/query#example-query-multi-mapping-one-to-many
why they have you store the orders in the dictionary
A row is returned for every OrderDetail. So you want to make sure to add the OrderDetail to the existing Order and not create a new one for every OrderDetail. The dictionary is used for performance to check if the Order has been already created or not.
it would be cleaner to just return the ordersDictionary.Values
How will your query return dictionary values?
Of course, if you are in a method such as yours, you can do
var list = orderDictionary.Values;
return list;
But how to make this Connection.Query return dictionary values? An order is returned for every row/OrderDetail, so the order will be returned multiple times.
Outside the Query, your dictionary solution works great and is even a better solution for performance, but if you want to make your Query return the distinct list of orders without using Distinct or some similar method, it's impossible.
EDIT: Answer comment
my suggestion return orderDictionary.Values.ToList(); //change 3 return dictionaryValues
Thank you for your great feedback, it's always appreciated ;)
It would be weird in a tutorial to use what the query returns when there is no relationship but use the dictionary for one to many relationships
// no relationship
var orders = conn.Query<Order>("", ...).Distinct();
// one to many relationship
conn.Query<Order, OrderDetail>("", ...);
var orders = orderDictionary.Values.ToList();
Your solution is better for performance the way you use it, there is no doubt about this. But this is how people usually use the Query method:
var orders = conn.Query("", ...).Distinct();
var activeOrders = orders.Where(x => x.IsActive).ToList();
var inactiveOrders = orders.Where(x => !x.IsActive).ToList();
They use what the Query method returns.
But again, there is nothing wrong with the way you do it, this is even better if you can do it.
How do I get the total results of a query that returns more than the limit?
I think that SQL API v2 had RetrievedDocumentCount in its QueryMetrics-object
https://learn.microsoft.com/sv-se/azure/cosmos-db/profile-sql-api-query#linq-on-documentquery
But I can't find that in v3. You use an iterator instead, so the code is very different. There is an Diagnostics-property but it does not seem to contain what I need.
IOrderedQueryable query = _container.GetItemLinqQueryable<T>();
FeedIterator<T> iterator = query.Limit(10).ToFeedIterator();
var documents = new List<T>();
while (iterator.HasMoreResults)
{
var currentResultSet = await iterator.ReadNextAsync();
var count = currentResultSet.Diagnostics.GetTotalResultsHerePlease();
documents.AddRange(currentResultSet);
}
I assume query is your IQueryable<>? (Please don't use var in pasted code, it really helps to be able to read the types.) In that case you can get the count by
int totalCount = await query.CountAsync();
See documentation here.
I have a solution for filtering on this question.
This works perfectly with a column that has string values. When I try to filter a column with numeric values it will not work. I'm assuming it is because .setHiddenValues() will not accept numeric values. I could be wrong.
Let me explain my scenario:
The user inputs a value on an HTML interface, let's say 6634.
The HTML calls my function on .gs and passes the numeric value the user inputted.
google.script.run //Executes an Apps Script JS Function
.withSuccessHandler(updateStatus) //function to be called upon successfull completion of Apps Script function
.withFailureHandler(failStatus)
.withUserObject(button) //To pass the event element object
.projectSearch2(projectID); //Apps Script JS Function
return;
The function (on the linked question above) will take that value and bump it up against the values in a column deleting the value if it is found. What I am left with is an array of values that I do not want filtered.
function projectSearch2(projectID){
var ss = SpreadsheetApp.getActive();
var monthlyDetailSht = ss.getSheetByName('Data Sheet');
var monLastCN = monthlyDetailSht.getLastColumn();
var monLastRN = monthlyDetailSht.getLastRow();
var data = monthlyDetailSht.getRange(1,1,1,monLastCN).getValues();//Get 2D array of all values in row one
var data = data[0];//Get the first and only inner array
var projectIDCN = data.indexOf('Project Id') + 1;
//Pull data from columns before filtering
var projectIDData = monthlyDetailSht.getRange(2,projectIDCN,monLastRN,1).getValues();
//Reset filters if filters exist
if(monthlyDetailSht.getFilter() != null){monthlyDetailSht.getFilter().remove();}
//Start Filtering
var projectIDExclCriteria = getHiddenValueArray(projectTypeData,projectID); //get values except for
var rang = monthlyDetailSht.getDataRange();
var projectIDFilter = SpreadsheetApp.newFilterCriteria().setHiddenValues(projectIDExclCriteria).build();//Create criteria with values you do not want included.
var filter = rang.getFilter() || rang.createFilter();// getFilter already available or create a new one
if(projectID != '' && projectID != null){
filter.setColumnFilterCriteria(projectIDCN, projectIDFilter);
}
};
function getHiddenValueArray(colValueArr,visibleValueArr){
var flatUniqArr = colValueArr.map(function(e){return e[0];})
.filter(function(e,i,a){return (a.indexOf(e.toString())==i && visibleValueArr.indexOf(e.toString()) ==-1); })
return flatUniqArr;
}
That array is used in .setHiddenValues() to filter on the column.
Nothing is filtered however. This works for all columns that contain string values, just not columns with numeric values. At this point I'm lost.
Attempted Solutions:
Convert user variable to string using input = input.toString(). Did not work.
manually inserted .setHiddenValues for projectIDExclCriteria. Like this: var projectIDFilter = SpreadsheetApp.newFilterCriteria().setHiddenValues([1041,1070,1071,1072]).build(); That succeeded so I know the issue is before that.
Step before was calling getHiddenValueArray. I manually inserted like so: var projectIDExclCriteria = getHiddenValueArray(projectIDData,[6634]); It is not working. Is the issue with that getHiddenValueArray function not handling the numbers properly?
Here is a solution. Changing the following:
.filter(function(e,i,a){return (a.indexOf(e.toString())==i && visibleValueArr.indexOf(e.toString()) ==-1); })
To:
.filter(function(e,i,a){return (a.indexOf(e) == i && visibleValueArr.indexOf(e) == -1); })
That works! Thank you Tanaike. The next question is will this impact columns that are not numeric. I have tested that and it works as well.
How about this modification?
From :
.filter(function(e,i,a){return (a.indexOf(e.toString())==i && visibleValueArr.indexOf(e.toString()) ==-1); })
To :
.filter(function(e,i,a){return (a.indexOf(e) == i && visibleValueArr.indexOf(e) == -1); })
Note :
In this modification, the number and string can compared using each value.
If you want to use return (a.indexOf(e.toString())==i && visibleValueArr.indexOf(e.toString()) ==-1), you can achieve it by modifying from colValueArr.map(function(e){return e[0];}) to colValueArr.map(function(e){return e[0].toString();}).
In this modification, colValueArr.map(function(e){return e[0].toString();}) converts the number to string, so the number is used as a string.
Reference :
Array.prototype.indexOf()
I have a requirement to display lists of newly-created and updated pages in our Episerver intranet - say the last ten of each. I've tried using FindPagesWithCriteria but this returns no results. Here's the code I've tried:
PageDataCollection recentPages;
PropertyCriteriaCollection criteria;
PropertyCriteria upperBound;
PropertyCriteria lowerBound;
criteria = new PropertyCriteriaCollection();
upperBound = new PropertyCriteria();
upperBound.Condition = CompareCondition.LessThan;
upperBound.Type = PropertyDataType.Date;
upperBound.Value = DateTime.Today.ToString();
upperBound.Name = "Created"; // Or Saved for updated pages
criteria.Add(upperBound);
lowerBound = new PropertyCriteria();
lowerBound.Condition = CompareCondition.GreaterThan;
lowerBound.Type = PropertyDataType.Date;
lowerBound.Value = DateTime.Today.AddDays(-7).ToString();
lowerBound.Name = "Created";
criteria.Add(lowerBound);
recentPages = DataFactory.Instance.FindPagesWithCriteria(PageReference.StartPage, criteria);
I've also tried using the RecentlyChangedPagesFinder (as detailed here) - this returns some results, but when I try to use the set of results to build a PageCollection to databind into a PageList, again I get nothing output. And I can't see that I could use that for new pages, only updated ones.
The property name should be "PageCreated".
http://epiwiki.se/developing/properties/all-built-in-properties
You can also improve your FindPagesWithCriteria-syntax by going something like this:
var criterias = new PropertyCriteriaCollection
{
new PropertyCriteria()
{
Name = "SomeProp",
Type = PropertyDataType.PageType,
Value = "eh",
Condition = CompareCondition.Equal,
Required = true
},
new PropertyCriteria()
{
...
};
var pages = DataFactory.Instance.FindPagesWithCriteria(somePageLink, criterias);