Lucene search not working - c#

I have a function which searches some articles in the Sitecore content items and give me the value. So far I have build up my indexes and it is showing in my IndexViewer. But the return of the function is 0. I looked up this link: http://sitecoregadgets.blogspot.com/2009/11/working-with-lucene-search-index-in_25.html for more information.
protected IEnumerable<Item> ShowHomePageNews(int numOfArticles, string stringofCountries)
{
List<Item> items = new List<Item>();
Sitecore.Search.Index indx = SearchManager.GetIndex("newsArticles");
using (IndexSearchContext searchContext = indx.CreateSearchContext())
{
var db = Sitecore.Context.Database;
CombinedQuery query = new CombinedQuery();
QueryBase catQuery = new FieldQuery("countries", stringofCountries); //FieldName, FieldValue.
SearchHits results = searchContext.Search(catQuery); //Searching the content items by fields.
SearchResultCollection result = results.FetchResults(0, numOfArticles);
foreach (SearchResult i in result)
{
items = result
.Where(r => !r.Title.StartsWith("*"))
.Select(r => db.GetItem(new Sitecore.Data.ItemUri(r.Url).ToDataUri()))
.ToList();
//Lucene.Net.Documents.Field url = i.Document.GetField("_url");
//Sitecore.Data.ItemUri itemUri = new Sitecore.Data.ItemUri(url.StringValue());
//Sitecore.Data.Items.Item item = Sitecore.Context.Database.GetItem(itemUri.ToDataUri());
//items.Add(item);
}
}
return items;
}
Over here the result is 0. What I am doing wrond here?
This is the snapshot of what I am seeing in my IndexViewer:
EDIT:
I am passing a "NZ" in the 'catQuery' and I am getting the result back. Because in my index viewer I am seeing the Field Name = _name, which contains NZ in it. I got this part. However, I want my every field to be indexed. I am seeing only 3 fields in my IndexViewer: _url, _group & _name.

So your countries should be tokenized by the indexer. As a multilist, they will be tokenized by GUID. Searching for a single country by GUID with your code above should work. However, if you want to search for multiple countries, where any of the passed in countries can trigger a match, you need to structure your query differently.
CombinedQuery query = new CombinedQuery();
//apply other filters here to query if need be
//and country filter by creating a new clause (combinedquery) and "ORing" within it (QueryOccurance.Should)
CombinedQuery query3 = new CombinedQuery();
//here you would actually iterate over your country list
query3.Add(new FieldQuery("countries", country1GUID), QueryOccurance.Should);
query3.Add(new FieldQuery("countries", country2GUID), QueryOccurance.Should);
query.Add(query3, QueryOccurance.Must);

Related

I try to make a lamda expression in C# for filtering a list and can't make it work when I compare a variable to an item in an array

So I have the gui set up so that I have two main listboxes. I'm still figuring out what kind of gui I want for this application so there is another one but that's not relevant. One listbox is a list of options for what you want to check for a department or an employee. The other is a list of departments. Right now I have the functionality for the name option to view the names of employees for a department. I just need to know how I can filter a list so that the only employees that show up are the ones who are in the chosen department after I click on the submit button. I figured I would use a lambda expression for that and it hasn't been working for me. I really want to know how to use lambda expressions better so please only give me a solution that involves using them. If it's impossible or if it would be more efficient to do something else then let me know.
File where I put reads and set dept array to file contents
//list of employees
public static List<Employee> EmployeeList = new List<Employee>();
//array to hold the options users have for interacting with info
public static string[] OptionsArr;
//array to hold the departments
public static string[] DeptsArr;
//skipping around same file to relevant code
//set the departments array to the contents of the depts file
DeptsArr = File.ReadAllLines("..\\..\\departments.txt");
Not sure if needed
Method for populating DeptListBox
private void UpdateDeptListBox()
{
//set up for new info
DeptListBox.Items.Clear();
//prevent interfence with update
DeptListBox.BeginUpdate();
//set department listbox to depts array
DeptListBox.DataSource = Program.DeptsArr;
DeptListBox.EndUpdate();
}
Problem Method - the submit button method
List<Employee> ResultList = new List<Employee>();
//name
if (OptionsListBox.SelectedIndex == 1)
{
//user selects Marketing department
if (DeptListBox.SelectedIndex == 0)
{
//problem is either with lambda exp or Program.DeptsArr comparison
foreach (Employee empl in Program.EmployeeList.Where(empl => empl.Dept.CompareTo(Program.DeptsArr[0]) == 0).ToList())
{
//this doesn't happen
ResultList.Add(empl);
}
for (int i = 0; i<ResultList.Count; i++)
{
ResultListBox.Items.Add(ResultList[i].Lname + " " + ResultList[i].Fname + " " + ResultList[i].Dept);
}
}
}
}
For me it can be helpful when I am having issues to break stuff down and look at smaller pieces. Are you sure the issue is your lambda function? It may be your options listbox != 1 or that the data is not being read in correctly.
As far as I can tell, this part should work. Although there are some issues with it:
foreach (Employee empl in Program.EmployeeList.Where(empl =>empl.Dept.CompareTo(Program.DeptsArr[0]) == 0).ToList())
{
//this doesn't happen
ResultList.Add(empl);
}
You could start with just the Employee lambda function and hard code the values. Maybe something like this which does indeed produce the correct results (Bob and Brandon)
List<Employee> ResultList = new List<Employee>();
List<Employee> EmployeeList = new List<Employee> {
new Employee{ Name = "Bob", Dept = "Accounting" },
new Employee{ Name = "Larry", Dept = "A" },
new Employee{ Name = "Margret", Dept = "B" },
new Employee{ Name = "Brandon", Dept = "Accounting" }
};
string[] DeptsArr = new string[2];
DeptsArr[0] = "Accounting";
DeptsArr[1] = "A";
//user selects Marketing department
if (departmentIndex == 0)
{
foreach (Employee empl in EmployeeList.Where(empl => empl.Dept.CompareTo(DeptsArr[0]) == 0).ToList())
{
ResultList.Add(empl);
}
}
However your lamda function inside a foreach loop is redundant. You can think of a lambda function as an instruction for running a foreach loop. A foreach loop by itself could look like this:
List<Employee> ResultList = new List<Employee>();
foreach (Employee empl in EmployeeList)
{
if(empl.Dept == DeptsArr[0])
{
ResultList.Add(empl);
}
}
You could get the same result as the foreach loop above, by using the following lamda function:
List<Employee> ResultList = EmployeeList.Where(empl => empl.Dept == DeptsArr[0]).ToList();
A final note is that the "ToList()" on the end of that lambda function is what executes the loop and returns the result as a List. Many times this is not required. Without the "ToList()" part an IEnumerable will be returned which you may be able to use instead. Using an IEnumerable instead of calling ToList() can have better performance in many scenarios.
If you want to test whether a specific value is in an array then you call Contains on that array, e.g.
var allEmployees = new List<Employee>();
// Populate allEmployees here.
var selectedEmployees = allEmployees.Where(e => selectedDepartments.Contains(e.Department)).ToArray();
The selectedEmployees array will contain only the Employee objects from the allEmployees list with a Department property value that is contained in the selectedDepartments array/collection.

C# scan list against master list for missing items

I have a master list that has the values for tables of a database I know to be correct:
masterList: List<string>(){ "business", "customer", "location", "employee", etc}
And I've queried a new database that is supposed to be identical. My test will tell me if I have any errors in the scripts my team has made to make this new DB. tablesList is supposed to the be the return of my query:
tablesList: List<string>(){ "business", "customer", "location", "employee", etc}
So in practice they are supposed to be the same, but to test errors, I want to compare the tablesList against the masterList to make sure all needed tables are there. As a copy of this process, I'm also reversing the search, in case there are any extra tables that are not there on the masterList.
Question: How do I compare a list against a master list, and return items that don't match up?
I am using Visual Studio 2017 with c# .net Core 2.0.
Here is what I've been trying so far:
var errorsList = new List<string>();
tablesList = QuerySchemaForTables();
masterList = GrabMasterTableList();
foreach(var item in masterList)
errorsList.Add(tablesList.Where(x => x.Contains(item)));
But with this, I'm getting the error:
cannot convert from IEnumerable to string
You can get the two directions of errors using LINQ. No need for the loop:
var missingInMasterList = tableList.Where(x => !masterList.Contains(x)).ToList();
var missingInTableList = masterList.Where(x => !tableList.Contains(x)).ToList();
Are you looking for something like that;
var errorList = tableList.Where(x => !masterList.Contains(x));
You can capture the differences using .Except(), which is one of the IEnumerable set operations:
var missingTables = masterList.Except(tablesList);
var extraTables = tablesList.Except(masterList);
Then, to create your error message, you can join the items in these IEnumerables with a comma into a single string using string.Join():
var errorMessage = new StringBuilder();
if (missingTables.Any())
{
errorMessage.AppendLine("Missing Tables: " + string.Join(", ", missingTables));
}
if (extraTables.Any())
{
errorMessage.AppendLine("Extra Tables: " + string.Join(", ", extraTables));
}
Then you can output your results by checking the length of errorMessage to determine if any errors were encountered:
if (errorMessage.Length > 0)
{
Console.WriteLine(errorMessage.ToString());
}
else
{
Console.WriteLine("No extra or missing tables detected");
}
I think better to use is Except() as follows
var MasterList = new List<string> { "business", "customer", "location", "employee"};
var ChildList = new List<String> { "customer", "location", "employee" };
var filter = MasterList.Except(ChildList);
This will values those are not in ChildList.You can also do vice versa.
To find all items that are in the tablesList but not in the masterList use .Contains:
var errorsList = tableList.Where(x => !masterList.Contains(x));
But I recommend you use a HashSet<String> for masterList so search for an item in it will be in O(1) instead of O(n):
var masterCollection = new HashSet<String>(GrabMasterTableList());
var errorsList = tableList.Where(x => !masterCollection.Contains(x));
As for the problem with your code as you posted:
foreach(var item in masterList)
errorsList.Add(tablesList.Where(x => x.Contains(item))); // <-- error
As the error points out, Linq's .Where returns an IEnumerable<T> whereas .Add expects a single item of the type of the collection, which in this case is a single string. You could use .AddRange instead but I think a better use all together is what I wrote above.
Your code is presently trying to add an IEnumerable to a List.
If you want to add all the matches you should AddRange instead.
https://msdn.microsoft.com/en-us/library/z883w3dc(v=vs.110).aspx
var errorsList = new List<string>();
tablesList = QuerySchemaForTables();
masterList = GrabMasterTableList();
foreach(var item in masterList)
errorsList.AddRange(tablesList.Where(x => x.Contains(item)));

LINQ - Deserialize JSON column and filter

How to deserialize/serialize a property with JSON string array value and then filter (using where clause) in LINQ inside a lambda expression?
void Main()
{
var regionList = new List<Row>() {
new Row { RegionJsonList = "[\"QLD\",\"NSW\"]" },
new Row { RegionJsonList = "[\"TAZ\",\"SA\"]" },
new Row { RegionJsonList = "[\"QLD\",\"VIC\"]" }
};
var filterRegionList = new List<string>() {
"QLD", "NSW"
};
var queryable = regionList.AsQueryable();
// this is obviously wrong, i just want to find the Row that contains one on filterRegionList
var result = queryable.Where(r => JsonConvert.DeserializeObject<string[]>(r.RegionJsonList).Contains(filterRegionList));
result.Count().Dump(); // should be 2
}
class Row
{
public string RegionJsonList { get;set; }
}
Following would work:
var result =
filterRegionList.Aggregate(regionList,(current,filter) =>
current.Where( r => r.RegionJsonList.Contains(filter)).ToList())
Aggregating the filterRegionList and regionList and thus applying filters for the final result. I did not find a requirement to Deserialize the RegionJsonList, since this would work as is, but you may add that part in case you are keen.
Also we are applying And filter via aggregation, it checks for the rows which contains both the filters, and thus provide the result, you may modify filter to achieve more number of rows, like following will select two entries from original regionList
var filterRegionList = new List<string>() { "QLD" };
To filter for rows that contain at least one of the entries from filterRegionList, you can use Enumerable.Intersect and check for non-empty intersections:
var resultAny = queryable.Where(r => JsonConvert.DeserializeObject<string[]>(r.RegionJsonList).Intersect(filterRegionList).Any());
To filter for rows that contain all of the entries from filterRegionList, you can use Enumerable.Except to remove the row's entries from the filter list. If everything gets removed, it's a match:
var resultAll = queryable.Where(r => !filterRegionList.Except(JsonConvert.DeserializeObject<string[]>(r.RegionJsonList)).Any());
(It wasn't entirely clear from your question which you wanted.)

Why is RemoveAll(x => x.Condition) removing all my records?

I'm working on creating a filter for a collection of employees. In order to do this I initially fetch a raw collection of all employees. I clone this list so I can iterate over the original list but remove items from the second list.
For each filter I have, I build a collection of employee ids that pass the filter. Having gone through all filters I then attempt to remove everything that isn't contained in any of these lists from the cloned list.
However for some reason, whenever I attempt to do this using .RemoveAll(), all records seemed to be removed and I can't figure out why.
Here is a stripped down version of the method I'm using, with only 1 filter applied:
public List<int> GetFilteredEmployeeIds(int? brandId)
{
List<int> employeeIds = GetFilteredEmployeeIdsBySearchTerm();
List<int> filteredEmployeeIds = employeeIds.Clone();
// Now filter the results based on which checkboxes are ticked
foreach (var employeeId in employeeIds)
{
// 3rd party API used to get values - please ignore for this example
Member m = new Member(employeeId);
if (m.IsInGroup("Employees"))
{
int memberBrandId = Convert.ToInt32(m.getProperty("brandID").Value);
// Filter by brand
List<int> filteredEmployeeIdsByBrand = new List<int>();
if (brandId != null)
{
if (brandId == memberBrandId)
filteredEmployeeIdsByBrand.Add(m.Id);
var setToRemove = new HashSet<int>(filteredEmployeeIdsByBrand);
filteredEmployeeIds.RemoveAll(x => !setToRemove.Contains(x));
}
}
}
return filteredEmployeeIds;
}
As you can see, I'm basically attempting to remove all records from the cloned record set, wherever the id doesn't match in the second collection. However for some reason every record seems to be getting removed.
Anybody know why?
P.S: Just to clarify, I have put in logging to check the values throughout the process and there are records appearing in the second list, however for whatever reason they're not getting matched in the RemoveAll()
Thanks
Ok only minutes after posting this I realised what I did wrong: The scoping is incorrect. What it should've been was like so:
public List<int> GetFilteredEmployeeIds(int? brandId)
{
List<int> employeeIds = GetFilteredEmployeeIdsBySearchTerm();
List<int> filteredEmployeeIds = employeeIds.Clone();
List<int> filteredEmployeeIdsByBrand = new List<int>();
// Now filter the results based on which checkboxes are ticked
foreach (var employeeId in employeeIds)
{
Member m = new Member(employeeId);
if (m.IsInGroup("Employees"))
{
int memberBrandId = Convert.ToInt32(m.getProperty("brandID").Value);
// Filter by brand
if (brandId != null)
{
if (brandId == memberBrandId)
filteredEmployeeIdsByBrand.Add(m.Id);
}
}
}
var setToRemove = new HashSet<int>(filteredEmployeeIdsByBrand);
filteredEmployeeIds.RemoveAll(x => !setToRemove.Contains(x));
return filteredEmployeeIds;
}
Essentially the removal of entries needed to be done outside the loop of the employee ids :-)
I know that you said your example was stripped down, so maybe this wouldn't suit, but could you do something like the following:
public List<int> GetFilteredEmployeeIds(int? brandId)
{
List<int> employeeIds = GetFilteredEmployeeIdsBySearchTerm();
return employeeIds.Where(e => MemberIsEmployeeWithBrand(e, brandId)).ToList();
}
private bool MemberIsEmployeeWithBrand(int employeeId, int? brandId)
{
Member m = new Member(employeeId);
if (!m.IsInGroup("Employees"))
{
return false;
}
int memberBrandId = Convert.ToInt32(m.getProperty("brandID").Value);
return brandId == memberBrandId;
}
I've just done that off the top of my head, not tested, but if all you need to do is filter the employee ids, then maybe you don't need to clone the original list, just use the Where function to do the filtering on it directly??
Please someone let me know if i've done something blindingly stupid!!

Specifying return rows in LINQ2DataSet

I have a requirement to extract a distinct subset of rows from a DataTable, and thought LINQ2DataSets may be a useful and clean way to do this, however it appears that it is not possible to simply identify return rows from a LINQ2DS query as follows
var result = from r in fips.AsEnumerable() select
r.Field<string>("FACILITY_PROCESS_SUB_GROUP_CODE"),
r.Field<string>("PROCESS_SUB_GROUP_NAME"),
r.Field<string>("...
as I start getting errors after the first comma.
Is this a correct assumption, and how would I get around it to return a subset of columns from the dataset that I can apply a Distinct() method to?
You forgot the new statement and field names:
var result = from r
in fips.AsEnumerable()
select new
{
FacProcess = r.Field<string>("FACILITY_PROCESS_SUB_GROUP_CODE"),
GroupName = r.Field<string>("PROCESS_SUB_GROUP_NAME"),
Item3 = r.Field<string>("Item3")
};
You can also explicitly declare that you are going to use a type:
var result = from r
in fips.AsEnumerable()
select new MyType("InitClassParams")
{
FacProcess = r.Field<string>("FACILITY_PROCESS_SUB_GROUP_CODE"),
GroupName = r.Field<string>("PROCESS_SUB_GROUP_NAME"),
Item3 = r.Field<string>("Item3")
};
Scott Guthrie (VP Developer Devision, Microsoft) has some good info about LINQ (he talks about LINQ to SQL, but most of it applies regardless).
Then apply the distinct clause:
var result = from r
in fips.AsEnumerable()
select new
{
FacProcess = r.Field<string>("FACILITY_PROCESS_SUB_GROUP_CODE"),
GroupName = r.Field<string>("PROCESS_SUB_GROUP_NAME"),
Item3 = r.Field<string>("Item3")
}
distinct;
Then put it to a list or iterate over it. Nothing will be selected/distincted/etc until something like on of the following is run:
var list = result.ToList()
foreach(var item in result) {}

Categories

Resources