Best approach find position of an item in Lucene search results - c#

I am using Lucene.NET and able to search get hit results as ScoreDoc[].
I need to know specific item position in ScoreDoc[]. All items in ScoreDoc[] are unique.
Sample code:
luceneSearcher.Search(query, collector);
ScoreDoc[] scores = collector.TopDocs().scoreDocs
For example, I need to get find item position in ScoreDoc[], which has custom ID property where value could be '99999'.
I can iterate through item in scores[] and check for ID property which matches '99999' then return the position, but this can have performance hit because scores[] can have thousands of items.
Is there any better technique?
Thanks

I came up with creating new ExtendedCollector which stores CollectedDocuments.
public class ExtendedCollector : Collector
{
private Scorer _scorer;
private Int32 _docBase;
private List<CollectedDocument> _documents;
public ExtendedCollector()
{
_documents = new List<CollectedDocument>();
}
public override void SetScorer(Scorer scorer)
{
_scorer = scorer;
}
public override void Collect(int doc)
{
var docId = _docBase + doc;
var score = _scorer.Score();
var currentDoc = _documents.FirstOrDefault(d => d.DocId == docId);
if (currentDoc == null)
_documents.Add(new CollectedDocument()
{DocId = docId, Score = score, OriginalIndex = _documents.Count, Index = _documents.Count});
else
currentDoc.Score = score;
}
public override void SetNextReader(IndexReader reader, int docBase)
{
_docBase = docBase;
}
public override bool AcceptsDocsOutOfOrder()
{
return false;
}
public List<CollectedDocument> Documents
{
get { return _documents; }
}
public List<CollectedDocument> DocumentsByScore
{
get
{
var result = _documents.OrderByDescending(d => d.Score).ToList();
var itemId = 0;
foreach (var collectedDocument in result)
{
itemId++;
collectedDocument.Index = itemId;
}
return result;
}
}
}
CollectedDocument looks like this
public class CollectedDocument
{
public Int32 DocId { get; set; }
public float Score { get; set; }
public int OriginalIndex { get; set; }
public int Index { get; set; }
}
Whenever you want to get results you would do
var myCollector = new ExtendedCollector();
searcher.Search(searchQuery, myCollector);
foreach (var doc in myCollector.Documents)
{
var docIndex = doc.Index; //this is the current index in a list
var originalIndex = doc.OriginalIndex; //this is item Id set when doc was collected
}
You can also get the documents ordered by score using
myCollector.DocumentsByScore
This might not be the easiest solution, but it works. If anyone has a better solution, please post it as I'd like to know that as well.

Related

How to change value in Data Transfer Object c#

I want to assign a value in my DTO to another value in another table. I have tried the following below but does not seem to be working. I want to change the deleted value in the DTO.Option to the deleted value in the QuestionOptions table. My code is below:
private List<DTO.Option> MapOptions(List<DAL.QuestionOption> o)
{
List<DTO.Option> op = new List<DTO.Option>();
foreach (DAL.QuestionOption opt in o)
{
{
DTO.Option Option = MapOption(opt.Option);
foreach (var i in o)
{
Option.Deleted = i.isDeleted;
}
op.Add(Option);
}
}
return op;
}
private DTO.Option MapOption(DAL.Option o)
{
return new DTO.Option()
{
ID = o.ID,
Text = o.Text,
Value = o.Value
};
}
CODE UPDATED TO
private List<DTO.Option> MapOptions(List<DAL.QuestionOption> o)
{
List<DTO.Option> op = new List<DTO.Option>();
foreach (DAL.QuestionOption opt in o)
{
op.Add(MapOption(opt.Option));
}
return op;
}
private DTO.Option MapOption(DAL.QuestionOption o)
{
return new DTO.Option()
{
ID = o.Option.ID,
Text = o.Option.Text,
Value = o.Option.Value,
Deleted = o.isDeleted
};
}
You're looping through the List<DAL.QuestionOption> inside a loop of the List<DAL.QuestionOption>, and reassigning the Deleted property for every one of them. Unless I'm mistaken, that's not your intention. You'd be left with every DTO.Option.Deleted being equal to the DAL.QuestionOption.isDeleted of the last item in the list. I'm guessing DAL.QuestionOption looks approximately like this:
public class DAL.QuestionOption
{
public DAL.Option Option { get; set; }
public boolean isDeleted { get; set; }
}
public class DAL.Option
{
public int Id { get; set; }
public string Text { get; set; }
public string Value { get; set; }
}
If so, I would just change the MapOption to take in the DAL.QuestionOption instead of DAL.Option:
private DTO.Option MapOption(DAL.QuestionOption o)
{
return new DTO.Option()
{
ID = o.Option.ID,
Text = o.Option.Text,
Value = o.Option.Value,
Deleted = o.isDeleted
};
}

Check property value in nested list with unknown levels

I have a nested menu - here a simplified class:
public class NestedNode
{
public string Url { get; set; }
public List<NestedNode> Children { get; set; }
}
Given that I have a recursive list of NestedNode, I'm trying to ascertain whether any descendant is active at any level.
Here's the code to test:
protected void Page_Load(object sender, EventArgs e)
{
// The url of the current page
var currentUrl = Request.Url.GetLeftPart(UriPartial.Path);
// This is a list of nested nodes
var nodes = SiloNodes;
// Start loop
RecursiveCall(nodes, currentUrl);
}
void RecursiveCall(IEnumerable<NestedNode> nodes, string currentUrl)
{
if (nodes == null) return;
foreach (var n in nodes)
{
// This can test current level only
//var isActive = n.Url == currentUrl;
// This can test next level down
//var isActive = n.Children.Any(c => c.Url == currentUrl);
// How can I test all levels in one go?
RecursiveCall(n.Children, currentUrl);
}
}
What I need to be able to do is work out if any of the parents children are active (at the top lavel) so that I can add classes. At the moment, my ideas only go one level deep.
How about something like
void Main()
{
var nodes = new List<NestedNode>();
var isActive = nodes.Any(n => n.AnyActive("url"));
}
public class NestedNode
{
public NestedNode()
{
Children = Enumerable.Empty<NestedNode>();
}
public string Url { get; set; }
public IEnumerable<NestedNode> Children { get; set; }
public bool AnyActive(string url){ return Url==url || Children.Any(c => c.AnyActive(url));}
}
In this situation I would probably add a method to the NestedNode to check the condition recursively - something like this:
public bool ExistsRecursive(Func<NestedNode, bool> predicate)
{
if(predicate(this))
{
return true;
}
foreach(var node in Children)
{
return predicate(node);
}
return false;
}
And then, in your Page_Load, all you need is this:
if(nodes.ExistsRecursive(n => n.Url == currentUrl))
{
// current url is found in at least one node
}

C# appending values to string[] doesn't work

So I'm trying to append values to a list (in Json []), that's empty or has items in it. So I check wether the list in the object has items in it or not, if the item doesn't exist, then it creates a new item, if it exists, it rewrites it's value. Here is the code:
if (e.Key == Key.Enter)
{
// When the user pressed enter, do action
Team selected_team = teams.Where(t => t.team_number == Convert.ToInt32(inp_team_number.Text)).FirstOrDefault();
if (selected_team != null)
{
// when the team number is given, go try and find the data of them
Results team_results = results.Where(r => r.team_number == Convert.ToInt32(inp_team_number.Text)).FirstOrDefault();
int index = (Convert.ToInt32(gtk_input.Name.Substring(gtk_input.Name.Length - 1)) - 1);
// Check if the item in the list exists
if (index < team_results.results[inp_tour_part.SelectedIndex].gtks.Length && team_results.results[inp_tour_part.SelectedIndex].gtks[index] != null)
{
if (regexColon.Match(gtk_input.Text).Success == true)
{
team_results.results[inp_tour_part.SelectedIndex].gtks[(Convert.ToInt32(gtk_input.Name.Substring(gtk_input.Name.Length - 1)) - 1)] = gtk_input.Text; // Give the new value
}
else
{
MessageBox.Show("Wrong value.", "An error occured", MessageBoxButton.OK, MessageBoxImage.Warning);
team_results.results[inp_tour_part.SelectedIndex].gtks[(Convert.ToInt32(gtk_input.Name.Substring(gtk_input.Name.Length - 1)) - 1)] = "00:00"; // Give the default value
}
}
else
{
if (regexColon.Match(gtk_input.Text).Success == true)
{
team_results.results[inp_tour_part.SelectedIndex].gtks.Append(gtk_input.Text); // Give the new value
}
else
{
MessageBox.Show("Wrong value.", "An error occured", MessageBoxButton.OK, MessageBoxImage.Warning);
team_results.results[inp_tour_part.SelectedIndex].gtks.Append("00:00"); // Give the default value
}
}
SaveResults(results);
// Move to the next UI element
MoveToNextUIElement(e);
}
else
{
MessageBox.Show("Something went somewhere wrong.", "An error occured", MessageBoxButton.OK, MessageBoxImage.Warning);
}
}
Now, it works fine to rewrite the items, but when the list is empty (default) or when the item doesn't exist, and it needs to add/append the new value, it doesn't crahs and doesn't throw any error... Also it doesn't add the value to my json, now when I initialize the new object for this, it looks like the following:
team_results = new Results()
{
team_number = selected_team.team_number,
results = new Result[2] { new Result{ }, new Result { } } // Fixed length of array for the results. TODO: Needs fix.
};
And the model looks like this:
namespace RittensportRekenSoftware.Models
{
public class Results
{
public int team_number { get; set; }
public Result[] results { get; set; }
}
public class Result
{
public string given_start_time { get; set; }
public string connection_to_start { get; set; }
public string start_kp { get; set; }
public string stop_kp { get; set; }
public int missed_controls { get; set; }
public float km { get; set; }
public string[] gtks { get; set; }
}
}
Now I just need a list of strings in my json, but I don't have any clue on how to achieve this...
If you MUST, you can resize an array using Array.Resize() method. Please see documentation here.
int[] array = new int[] { 1, 2, 3 };
Array.Resize(ref array, 5);
array[3] = 4;
array[4] = 5;
But it is strongly advised the use of List<T> instead of arrays. After all List<T> uses arrays behind the scenes so you get all the functionality of an array minus most of the cons.
You can use List instead. Thus you don't need to know the array size when instantiating the list.
Why not alter your models so they implement List instead of array. In the constructor of each model just initialize the empty list (or other action depending on your situation)
namespace RittensportRekenSoftware.Models
{
public class Results
{
public int team_number { get; set; }
public List<Result> results { get; set; }
public Results() {
results = new List<Result>();
}
}
public class Result
{
public string given_start_time { get; set; }
public string connection_to_start { get; set; }
public string start_kp { get; set; }
public string stop_kp { get; set; }
public int missed_controls { get; set; }
public float km { get; set; }
public List<string> gtks { get; set; }
public Result() {
gtks = new List<string>();
}
}
}
Then when you have your models you can add to each list like the following:
Results r = new Results();
r.results.Add(new Result()); // or other `result` object here
Result r = new Result();
r.gtks.Add("My String"); // or other `string` here
I think you can implement a method which would create a new array based on original one. Then, you would be able to override that original array with the resulted one (returned by that new method).
The example code would be the following:
var test = new string[1] { "Test string 1" };
test = AddItemToArray(test, "Test string 2");
private static string[] AddItemToArray(string[] original, string item)
{
var result = new string[original.Length + 1];
for (int i = 0; i < original.Length; i++)
{
result[i] = original[i];
}
result[result.Length - 1] = item;
return result;
}

How to drill into List and get their parentIds

I am facing an issue where I have to drill down through the list till I find the desired contentId. Once the contentId is matched, I need to get its ParentIds. I am able to get the contentId but not its ParentId.
Currently I am using recursion to get the ParentIds of a child node. But failed to get the desired results.
Can anyone make it run, or provide the correct code to get this issue fixed. I'm trying to get the childnode and its parentIds. I need to get the parentIds and then want to insert into a List.
I'm able to drill down into the loop but don't know how and when to store the parentIds into a list.
In this code, I am trying to get the parents of contentId "5".
class Program
{
static void Main(string[] args)
{
Program obj = new Program();
var data = obj.GetAllChildCats();
foreach (var item in data)
{
Console.WriteLine(item);
}
Console.ReadLine();
}
public List<int> GetAllChildCats()
{
var ret = getdata();
var data = GetAllChildCats(4, ret.contentObjects, 0);
return data;
}
List<int> parentIdsList1 = new List<int>();
private List<int> GetAllChildCats(int id, ContentObjects data, int Parentid)
{
if (!string.IsNullOrEmpty(data.ContentObjectId.ToString()))
{
parentIdsList1.Add(Parentid);
if (data.ContentObjectId == id)
{
return parentIdsList1;
}
else
{
if (data.ChildContentObjects != null)
{
foreach (ContentObjects cat in data.ChildContentObjects)
{
GetAllChildCats(id, cat, data.ContentObjectId);
}
}
}
}
return parentIdsList1;
}
public Heirarchy getdata()
{
Heirarchy ret = new Heirarchy()
{
_id = 11,
contentObjects = new ContentObjects()
{
ContentObjectId = 1,
NodeId = 34,
ChildContentObjects = new List<ContentObjects>()
{
new ContentObjects() {
ContentObjectId=2,
NodeId=34,
ChildContentObjects= new List<ContentObjects>()
{
new ContentObjects() {
ContentObjectId=3,
NodeId=34,
ChildContentObjects= null
}
}
},
new ContentObjects() {
ContentObjectId=4,
NodeId=34,
ChildContentObjects= new List<ContentObjects>()
{
new ContentObjects() {
ContentObjectId=5,
NodeId=34,
ChildContentObjects= null
}
}
},
}
},
HierarchyId = 2
};
return ret;
}
}
public class Heirarchy
{
public int _id { get; set; }
public ContentObjects contentObjects { get; set; }
public int HierarchyId { get; set; }
}
public class ContentObjects
{
public int ContentObjectId { get; set; }
public int NodeId { get; set; }
public List<ContentObjects> ChildContentObjects { get; set; }
}
In this code, I am trying to get the parents of contentId "5".
This can be solved by a simple depth-first search. We just have to check the children's ID before digging deeper into the tree, so that we can still return the parent reference:
static void Main(string[] args)
{
Program obj = new Program();
var parents = obj.GetParentsOf(5, obj.getdata().contentObjects);
Console.WriteLine(parents.Count()); // yields 1
Console.WriteLine(parents.First().ContentObjectId); // yields 4
Console.ReadLine();
}
private IEnumerable<ContentObjects> GetParentsOf(int id, ContentObjects root)
{
if (root.ChildContentObjects != null)
{
foreach (ContentObjects c in root.ChildContentObjects)
{
// If a direct child has the requested ID, we are a parent.
if (c.ContentObjectId == id)
{
yield return root;
}
// Recurse deeper down.
foreach (ContentObjects found in GetParentsOf(id, c))
{
yield return found;
}
}
}
}
If, on the other hand, by "parents" you mean the complete path down the tree, we need to modify the method as follows. We again have a recursive depth-first search, but we insert our own id into the path when returning from a successful recursive step:
static void Main(string[] args)
{
Program obj = new Program();
var path = obj.GetPathTo(5, obj.getdata().contentObjects);
// prints 1, 4
foreach (ContentObjects o in path)
{
Console.WriteLine(o.ContentObjectId);
}
Console.ReadLine();
}
// returns null if id could not be found
private IEnumerable<ContentObjects> GetPathTo(int id, ContentObjects root)
{
if (root.ChildContentObjects != null)
{
foreach (ContentObjects c in root.ChildContentObjects)
{
if (c.ContentObjectId == id)
{
// If a direct child has the requested ID, we are the first parent.
return new[] { root };
}
else
{
// Recurse deeper down.
var found = GetPathTo(id, c);
if (found != null)
{
// We found something deeper down. Since we are part of the
// path, append own id.
return new[] { root }.Concat(found);
}
}
}
}
return null;
}

complex way for update generic data model with reflection and collections

I am standing on a complex issue for me. I need update some models, but I like to work with a generic class not to rewrite some code individually.
I need to update data that have lists on their properties, with possible exclusion or inclusion of items on these lists, but these lists can be of any other class / type. My questions are commented on the code.
These models are unrealistic and a bit absurds but have similarities with my real models, note that the logic is reversed on these relationships during updates.
Thanks for all.
public class RedNotebook
{
[Key]
public int Id { get; set; }
public string PageTitle { get; set; }
public virtual ICollection<Signature> Signatures { get; set; }
}
public class BlueNotebook
{
[Key]
public int Id { get; set; }
public DateTime Entrance { get; set; }
public DateTime Leave { get; set; }
public virtual ICollection<Guest> GuestList { get; set; }
}
public class Signature
{
[key]
public int Id { get; set; }
public string PeopleSignature { get; set; }
public int IdRedNotebook { get; set; }
public int IdBlueNotebook { get; set; }
[ForeignKey("IdRedNotebook")]
public virtual RedNotebook { get; set; }
[ForeignKey("IdBlueNotebook")]
public virtual BlueNotebook { get; set; }
}
public class Guest
{
[key]
public int Id { get; set; }
public string Name { get; set; }
public int SeatNumber { get; set; }
public int IdBlueNotebook { get; set; }
[ForeignKey("IdBlueNotebook")]
public virtual BlueNotebook { get; set; }
}
/**********************/
public void UpdateData(T newData, out string msg)
{
try
{
var propId = newData.GetType().GetProperty("Id");
if (propId == null)
{
msg = "Unable to identify the identity of the reported data.";
return;
}
int id = Convert.ToInt32(propId.GetValue(newData));
if (id <= 0)
{
msg = "Unable to identify the identity of the reported data.";
return;
}
//instance a determined DbContext and Model<T>
var contexto = new CtxCliente(DAO.Classes.Util.InstanciarConexao(strCripto, (DAO.Conectores) Conector));
var model = contexto.Set<T>();
var targetData = model.Find(id);
if (targetData == null)
{
model.Add(newData);
contexto.Entry(model).State = EntityState.Added;
msg = "An addition was made because there was no previous reference.";
}
if (Convert.ToInt32(targetData.GetType().GetProperty("Id").GetValue(targetData)) > 0)
{
contexto.Entry(targetData).CurrentValues.SetValues(newData);
contexto.Entry(targetData).State = EntityState.Modified;
msg = string.Empty;
}
//TODO - 1) GET THE VIRTUAL PROPERTIES OF WHICH TYPE targetData ICollection
//TODO - 2) COMPARE THE CONTENT OF VIRTUAL PROPERTIES OF targetData WITH THE CONTENTS OF VIRTUAL PROPERTIES UPDATE, BOTH ICollection
//TODO - 3) REMOVE EXCESS OF targetData AND / OR ADD THAT AS THE CASE MAY BE MISSING (A - CLEAR DIFFERENCE, B - ADD DIFFERENCE)
//through the properties to identify those that are of the collection type
foreach (var propertytargetData in targetData.GetType().GetProperties())
{
if (!propertytargetData.PropertyType.IsGenericType)
continue;
var propsNewData = newData.GetType().GetProperty(propertytargetData.Name);
#region
//if all list items were removed on update
if (propsNewData == null && propertytargetData != null)
{
// NOT TESTED, MAYBE NOT WORK CORRECTLY
propertytargetData.SetValue(targetData,null);
}
//If an item was included or removed
else if (propsNewData != null)
{
var valTargetData = propertytargetData.GetValue(targetData);
var valNewData = propsNewData.GetValue(newData);
var listItemsTargetData = (IEnumerable) valTargetData;
var listItemsNewData = (IEnumerable) valNewData;
int countItemsTargetData = listItemsTargetData.Cast<object>().Count();
int countItemsNewData = listItemsNewData.Cast<object>().Count();
if (countItemsTargetData > countItemsNewData) //remove discarded
{
foreach (var itemtargetData in listItemsTargetData)
{
var idItemtargetData = itemtargetData.GetType().GetProperty("Id").GetValue(itemtargetData);
var existing = (from object itemListNewData in listItemsNewData
select itemListNewData.GetType().GetProperty("Id").GetValue(itemListNewData))
.Any(iditemListNewData => (int) idItemtargetData == (int) iditemListNewData);
if (!existing) //remove
{
//how to remove from the list?????? (targetData)
}
else //update
{
foreach (var itemListNewData in listItemsNewData)
{
var props = itemListNewData.GetType().GetProperties();
foreach (var propertyInfo in props)
{
foreach (var item in listItemsTargetData)
{
var p = item.GetType().GetProperty(propertyInfo.Name);
if (p != null && !p.PropertyType.IsGenericType)
{
p.SetValue(item, propertyInfo.GetValue(itemListNewData));
}
}
}
}
}
}
}
else if (countItemsTargetData < countItemsNewData) //Items need to be included
{
foreach (var newItem in listItemsNewData)
{
var idnewItem = newItem.GetType().GetProperty("Id").GetValue(newItem);
if ((int) idnewItem == 0)
{
//how to insert in list???????? (targetData)
}
else // remove and/or update some before (reduntant!?)
{
foreach (var itemtargetData in listItemsTargetData)
{
var idItemtargetData = itemtargetData.GetType().GetProperty("Id").GetValue(itemtargetData);
var existing = (from object itemListNewData in listItemsNewData
select itemListNewData.GetType().GetProperty("Id").GetValue(itemListNewData))
.Any(iditemListNewData => (int)idItemtargetData == (int)iditemListNewData);
if (!existing) //remove
{
//how to remove from the list?????? (targetData)
}
else //update
{
foreach (var itemListNewData in listItemsNewData)
{
var props = itemListNewData.GetType().GetProperties();
foreach (var propertyInfo in props)
{
foreach (var item in listItemsTargetData)
{
var p = item.GetType().GetProperty(propertyInfo.Name);
if (p != null && !p.PropertyType.IsGenericType)
{
p.SetValue(item, propertyInfo.GetValue(itemListNewData));
}
}
}
}
}
}
}
}
}
}
}
contexto.SaveChanges(); //save data on model
}
catch(...){}
}
Haven't tested it . But it should work if both source and dest implement the same ICollection interface and T has an Id property of type System.Int32. It uses the new dynamic keyword that enables you to do duck typing ;
private class IdComparer : IEqualityComparer<object>
{
public bool Equals(object x, object y)
{
//return ((dynamic) x).Id = ((dynamic) y).Id; //previous with convertion error
return ((dynamic) x).Id == ((dynamic) y).Id;
}
public int GetHashCode(object obj)
{
return ((dynamic) obj).Id;
}
}
private static void Copy(IEnumerable source, IEnumerable dest)
{
var cmp = new IdComparer();
var toRemove = dest.Cast<object>().Except(source.Cast<object>(),cmp).ToList();
var toAdd= source.Cast<object>().Except(dest.Cast<object>(),cmp).ToList();
foreach(var item in toAdd)
{
// dynamic runtime tries to find method that matches signiture void Add(T value so we add dummy variable so that it knows to search for bool Add(T value)
var dummy= ((dynamic) dest).Add(item);
}
foreach (var item in toRemove)
{
var dummy= ((dynamic)dest).Remove(item);
}
}

Categories

Resources