I have the following structure/code that I’m trying to fix one issue in it. It could have been done differently from the start but it is what it is.
When searching to get all employees by department id, the employees should be returned in the search with ASC order of the tag except when the tag is null, which should send it to the bottom of the search.
this is the relationship of the structure
Employee | Department ID | Department Name | Tag |
---|---|---|---|
Sam | 101 | Accounting | “aaa” |
Sam | 102 | Shipping | Null |
Tim | 101 | Accounting | “eee” |
Tim | 102 | Shipping | “zzz” |
and json view:
{
"Name": "Sam",
"Departments": [
{
"ID": 101,
"Name": "Accounting",
"Tag": "aaa"
},
{
"ID": 102,
"Name": "Shipping",
"Tag": Null
},
]
},
{
"Name": "Tim",
"Departments" : [
{
"ID": 101,
"Name": "Accounting",
"Tag": "eee"
},
{
"ID": 102,
"Name": "Shipping",
"Tag": "zzz"
},
]
}
So, if I’m searching for all employess that belong to account department (101), since Sam has a value for tagNumber “aaa” and it is preceding Tim’s whose tag is: “eee” then Sam should return first.
But if we are searching for Shipping department (102), Sam’s tagnumber in that department is null; and Tim’s has a tagnumber value for that department, then Tim should show up first.
When searching by Accounting ID (101) ASC --> Tag number determines order (aaa is before eee)
Sam
Tim
When searching by Shipping ID (102) ASC --> Tag number Null goes to bottom. zzz has precedence over null and therfore, Tim should show up first
Tim
Sam
This is how the docs and fields are created currently:
var doc = new Document();
doc.Add(new Field("Name", "Sam", fieldStored));
doc.Add(new Field("Department.Id", 101, fieldStored));
doc.Add(new Field("Department.Name", "Accounting", fieldStored));
doc.Add(new Field("Department.Tag", "aaa", fieldStored));
doc.Add(new Field("Department.Id", 102, fieldStored));
doc.Add(new Field("Department.Name", "Shipping", fieldStored));
doc.Add(new Field("Department.Tag", null, fieldStored));
var doc = new Document();
doc.Add(new Field("Name", "Tim", fieldStored));
doc.Add(new Field("Department.Id", 101, fieldStored));
doc.Add(new Field("Department.Name", "Accounting", fieldStored));
doc.Add(new Field("Department.Tag", "eee", fieldStored));
doc.Add(new Field("Department.Id", 102, fieldStored));
doc.Add(new Field("Department.Name", "Shipping", fieldStored));
doc.Add(new Field("Department.Tag", "zzz", fieldStored));
My search is executed as follows in the TagComparer class
public class TagComparator : FieldComparer<BytesRef>
{
private readonly BytesRef[] bvalues;
..........................
public override FieldComparer SetNextReader(AtomicReaderContext context)
{
sortedResults = FieldCache.DEFAULT.GetTermsIndex(context.AtomicReader, field);
return this;
}
public override void Copy(int slot, int doc)
{
termCopy = new BytesRef();
sortedResults.Get(doc, termCopy);
bvalues[slot] = termCopy;
}
}
ISSUE SUMMARY:
Right now, it returns “Sam” before Tim for Shipping Department (102) even though Sam’s tag number is Null and “Tim” later even though he has value of the tagnumber for that department “zzz”.
MY ATTEMPT:
Since my servers have many resources allocated, I can sacrifice a little on the search speed by adding more code to the Copy override method. The solution will mean:
-
store the department id before searcher code takes over
-
Copy method will execute once for every hit of user who belong to that department
-
get the whole doc of the hit that shows department id and tagnumber value
-
if tagnumber has legit value, it shows normall, if not, it shows in the bottom
<code> for (int i = 0; i < wholeDoc.GetValues("Department.Id").Length; i++){if(int.Parse(wholeDoc.GetValues("Department.Id")[i]) == 1234) // --> SOLUTION{string tagNumber= wholeDoc.GetValues("TagNumber")[i];if (string.IsNullOrWhiteSpace(tagNumber))SetBottom(doc);elsebvalues[slot] = new BytesRef(wholeDoc.GetValues("TagNumber")[i]);break;}}</code><code> for (int i = 0; i < wholeDoc.GetValues("Department.Id").Length; i++) { if(int.Parse(wholeDoc.GetValues("Department.Id")[i]) == 1234) // --> SOLUTION { string tagNumber= wholeDoc.GetValues("TagNumber")[i]; if (string.IsNullOrWhiteSpace(tagNumber)) SetBottom(doc); else bvalues[slot] = new BytesRef(wholeDoc.GetValues("TagNumber")[i]); break; } } </code>for (int i = 0; i < wholeDoc.GetValues("Department.Id").Length; i++) { if(int.Parse(wholeDoc.GetValues("Department.Id")[i]) == 1234) // --> SOLUTION { string tagNumber= wholeDoc.GetValues("TagNumber")[i]; if (string.IsNullOrWhiteSpace(tagNumber)) SetBottom(doc); else bvalues[slot] = new BytesRef(wholeDoc.GetValues("TagNumber")[i]); break; } }
This solution works (I’ve tested it against small subset of data) But the problem is with step #1. I don’t know what the Department.Id will be each time for the search. Once the search library codes executes, I couldn’t find a way to remember what department id was used. I’ve tried session variables, but they don’t work with external libraries. This is the last line that know of the department id being passed, but after that it is darkness. How do I pass the department id beyong this line?
var topDocs = searcher.Search(query, filter, (pageNumber * recordsPerPage), sort);