-
-
Notifications
You must be signed in to change notification settings - Fork 219
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(9586): implement freetext search in cht datasource #9625
base: master
Are you sure you want to change the base?
feat(9586): implement freetext search in cht datasource #9625
Conversation
eba7aac
to
43efbef
Compare
…text-search-in-cht-datasource
…text-search-in-cht-datasource
…text-search-in-cht-datasource
@jkuester the e2e tests related to cht-datasource are failing right now. I remember seeing something related to changes in how we deal with auth and its impact in cht-datasource. Do we have any workaround at the moment? |
Oh wow. I did not realize that the end-result of the changes was to just delete the existing remote cht-datasource tests with no alternative. This is very disappointing. We can do better here, though. The challenge is that in a browser context, the session cookie is automatically included when making the remote 1. Update cht-datasource to accept auth information when creating a remote DataContextThis would essentially be addressing #9701. We could pass username/password in the As I noted on the ticket, though, I am reluctant to make changes to the implementation code just for testing purposes. 2. Re-introduce the MITM for
|
@jkuester I did not go with the hook implementation as it would mean checking for filenames to run because this auth setup needs to run only for a few test suites, which would have resulted in a not-so-fruitful hook file with checks. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright! We are getting very close here.
freetext: Nullable<string> = null, | ||
type: Nullable<string> = null | ||
) => ctx.bind(Contact.v1.getIds)(Contact.v1.createQualifier(freetext, type)), | ||
getUuids: ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue: I guess this should be getUuidsByTypeFreetext
. Then we also need:
getUuidsPageByType
getUuidsByType
getUuidsPageByFreetext
getUuidsByFreetext
Right? I am still open to other ideas here since this full permutation approach is not very scalable. 😞 But, I am not sure what else to do except for accepting Qualifier parameters in this API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only other alternative I could think of is to make every parameter optional and process according to the non-empty parameters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FTR, after further discussion on the squad call, we decided to go forwards with having specific functions for each permutation of search parameters that we want to support. This will result in a lot of functions, but the amount of duplicated code will be limited since it will all be just calling through to the same core api functions. Additionally, being able to have verbose names for each function will improve the readability/clarity of this api. This seems like an opportunity to find simplicity by expanding horizontally instead of vertically (by nesting a bunch of optional params into the same qualifier object).
const allContacts = person ? [person, ...fetchedContacts] : fetchedContacts; | ||
const contactsWithHydratedPrimaryContact = contacts.map( | ||
hydratePrimaryContact(allContacts) | ||
).filter(item => item ?? false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue: why was this check included? I don't think we want to filter the null
values here. That is part of the horrible jankyness here with the lineage where we might not actually find docs for all the levels of a contacts hierarchy. But, if a level is missing, we want to show that with null
and not just drop the entry so it looks like there was no level there at all...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. I was not aware of this. When implementing this for the first time in shared-libs/cht-datasource/src/local/contact.ts
there was a case for person
typed contact in which null
values were being returned which was not happening in the corresponding shared-libs/cht-datasource/src/local/person.ts
so, I thought null
values were not required.
I'll remove the filter from here.
}; | ||
|
||
/** @internal */ | ||
export const getContactLineage = (medicDb: PouchDB.Database<Doc>) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: I guess now the getPrimaryContactIds, hydratePrimaryContact, hydrateLineage functions do not need to be exported since they are only getting used in this file.
data: pagedDocs.data.map((doc) => doc._id), | ||
cursor: pagedDocs.cursor | ||
}; | ||
return await getPaginatedDocs(getDocsFn, limit, skip); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue: okay bad news 😞 I found an edge case that breaks this logic... We should never get null id values back from these freetext queries, but it is possible to get duplicate id values.
To test this, I created a contact with:
"name": "Alberto O'Kon Rivera",
"short_name": "River",
Then I did a search with freetext=river
. The list of ids returned contained two instances of the same id value for this contact. This is because the getByStartsWithFreetext
query will match the emissions for both River
and Rivera
(as intended). To make things even worse, even if we dupe-checked the ids returned for a given page, I don't think there is any reason that Couch could not give us more dupes of the same id later on different pages (because the view will return things ordered by key not by id).... 😬
@sugat009 definitely interested to hear your thoughts on the best way to proceed here. Pragmatically, my current inclination is to guarantee that each page will be free from duplicates, but then note in our documentation that different pages could contain the same ids. (I cannot come up with any feasible way to guarantee no dupes across pages).
The good news is that if we decide to just dupe-check on a page-by-page basis, then I think we can easily just reuse the fetch and filter logic here like this:
const uuidSet = new Set<string>();
const filterFn = (uuid: Nullable<string>): boolean => {
if (!uuid) {
return false;
}
const { size } = uuidSet;
uuidSet.add(uuid);
return uuidSet.size !== size;
};
return await fetchAndFilter(
getDocsFn,
filterFn,
limit
)(limit, skip);
(We just need to update the fetchAndFilter
signature and change <T extends Doc>
to just be <T>
. I don't think we actually need the extends Doc
for the current functionality.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same thing is going to apply for the contact logic too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, after a bunch more investigation, I still believe the best approach is to ensure there are no duplicate entries on each page, but allow duplicate entries to be returned across pages. This is slightly better than in the current implementation of shared-libs/search
where, as far as I can tell, the default page limit was 50
and there was no dupe resolution at all for single-request search queries (so it would have been possible to get duplicate results back even on the same page).
For the record, here are the various approaches I considered to try and avoid returning duplicate entries across pages (and why each of them will not work):
- Edit the view code to only emit once for each contact.
- This would just break freetext searching since it depends on mapping multiple search terms to the same contact.
- Use a
reduce
function on the view to combine duplicate results.- To properly support freetext searches, we need to emit multiple keys for the same contact. The "duplicate" results are produced at runtime and are caused by doing a "starts-with" search across a range of keys. I do not see any way to use a
reduce
to join results for a contact that would be useful for a "starts-with" type search.
- To properly support freetext searches, we need to emit multiple keys for the same contact. The "duplicate" results are produced at runtime and are caused by doing a "starts-with" search across a range of keys. I do not see any way to use a
- Emit the contact
_id
value as part of the key. Then the query results would be sorted by contact id.- You cannot use the
_id
as the first value in a key array (unless you wanted to only search for entries for a particular id). Otherwise you would need to match all for the first element in the array and that would prevent filtering by any subsequent elements in the array. Thestart_key
/end_key
boundaries would essentially just include everything. - You cannot emit the
_id
value as part of the key (concatenated to the actual search key). If you put the_id
at the beginning of the key value, it would break the "starts-with" range search functionality. If you put the_id
at the end of the key value, there still would be no way to guarantee no duplicates because the results would be sorted by key and the same id value might be later in the results after a slightly different search term.
- You cannot use the
- Just pull back all the ids and filter them for duplicates.
- Technically this would work, but would totally defeat the purpose of having a paged API. It would also be much less performant than our current approach.
Co-authored-by: Joshua Kuestersteffen <[email protected]>
Co-authored-by: Joshua Kuestersteffen <[email protected]>
Co-authored-by: Joshua Kuestersteffen <[email protected]>
Co-authored-by: Joshua Kuestersteffen <[email protected]>
…thub.com:medic/cht-core into 9586-implement-freetext-search-in-cht-datasource
Co-authored-by: Joshua Kuestersteffen <[email protected]>
Co-authored-by: Joshua Kuestersteffen <[email protected]>
Co-authored-by: Joshua Kuestersteffen <[email protected]>
…thub.com:medic/cht-core into 9586-implement-freetext-search-in-cht-datasource
Description
Closes: #9586
Code review checklist
Compose URLs
If Build CI hasn't passed, these may 404:
License
The software is provided under AGPL-3.0. Contributions to this project are accepted under the same license.