-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Add WrappedCandidateMatcher for composing matchers #13109
Add WrappedCandidateMatcher for composing matchers #13109
Conversation
This PR has not had activity in the past 2 weeks, labeling it as stale. If the PR is waiting for review, notify the [email protected] list. Thank you for your contribution! |
Thanks @bjacobowitz for a very detailed pull request here! I'm not really familiar with this area of the code but intuitively would be curious what the alternative (you mentioned) of increasing the visibility on |
The |
This PR has not had activity in the past 2 weeks, labeling it as stale. If the PR is waiting for review, notify the [email protected] list. Thank you for your contribution! |
@romseygeek On further reflection, I think protected visibility may not be sufficient for a practical solution here, even when using subclasses. I'll again use
To access and invoke the implementation of matchQuery, we would need to create a subclass of the concrete We could approach a solution using an alternate factory implementation that would return a subclass of a known concrete type with hidden functions exposed. For example, we could create a subclass of The problem with that approach is that it breaks down in cases where the concrete implementation is not available to be subclassed, as is the case for several anonymous Ideally we would like a factory that returns a more generic class that extends the functionality of a Of course another solution would be to simply make these functions public, but I understand the desire to avoid that if possible. |
This PR has not had activity in the past 2 weeks, labeling it as stale. If the PR is waiting for review, notify the [email protected] list. Thank you for your contribution! |
Again, I'm not really familiar with this area of the code but would be curious what making the functions public would look like and if making them public is necessarily problematic. And echo-ing @mkhludnev's apache/solr#2315 (comment) comment w.r.t. just-compile test to retain extensibility in future. |
This PR has not had activity in the past 2 weeks, labeling it as stale. If the PR is waiting for review, notify the [email protected] list. Thank you for your contribution! |
@romseygeek I'm wondering if maybe we should make those functions Right now, outside of the
If we're thinking about making Again I'll use I think this would solve this issue without requiring us to dramatically change function permissions. The only additional requirement would be to expose some implementing CandidateMatcher classes, but I think that might be okay (we've already exposed ParallelMatcher and PartitionMatcher). Should I go ahead with this approach? |
Hi @bjacobowitz, thanks for the detailed update! I think this would be easier to reason about if we had some concrete examples. Do you think you could post some code of composite matcher implementation that you would like to implement? Doesn't even need to compile, but just to give me an idea of precisely what you're trying to achieve and how the current library code gets in the way of that. |
Sure. It's roughly the same approach as ParallelMatcher, but with a reactive setup instead of an ExecutorService.
It's something sort of like this: package com.my.custom.package;
public class MyReactiveMatcher<T extends QueryMatch> extends CandidateMatcher<T> {
public static class MatchTask {
final String queryId;
final Query matchQuery;
final Map<String, String> metadata;
public MatchTask(String queryId, Query matchQuery, Map<String, String> metadata) {
this.queryId = queryId;
this.matchQuery = matchQuery;
this.metadata = metadata;
}
}
private final List<MatchTask> tasks = new ArrayList<>();
// Factory of CandidateMatcher to which we will delegate matching
private final MatcherFactory<T> factory;
public MyReactiveMatcher(IndexSearcher searcher, MatcherFactory<T> factory) {
super(searcher);
this.factory = factory;
}
@Override
public void matchQuery(String queryId, Query matchQuery, Map<String, String> metadata) {
// Register a query to try to match later
tasks.add(new MatchTask(queryId, matchQuery, metadata));
}
// Run matchQuery
private static <T extends QueryMatch> MultiMatchingQueries<U> getResult(
MatchTask task, CandidateMatcher<T> matcher) {
try {
// This call is not possible outside the lucene.monitor package:
matcher.matchQuery(task.queryId, task.matchQuery, task.metadata);
} catch (IOException e) {
// This call is not possible outside the lucene.monitor package:
matcher.reportError(task.queryId, e);
}
// This call is not possible outside the lucene.monitor package:
return matcher.finish(0, 0);
}
@Override
protected void doFinish() {
// Kick off matching, with results coming back through the Flux as they are finished
Flux.fromIterable(tasks).flatMap(t -> getResult(t, factory.createMatcher(searcher))).doOnNext(::aBunchOfStuff).subscribe();
} The To use your suggestion of a subclass, I could create a |
Thanks, that's very helpful. Given that composite matchers need to call those methods for CandidateMatcher to be useful, I think the simplest way forward here is to make |
Great, I'll open another PR shortly with those changes ( |
Closing, see PR #13632 with new approach |
Description
Create a
WrappedCandidateMatcher
, along with a factory for creating these objects and a default implementation of the factory, to assist users in creating their own composite matchers.Problem
The current access protections on
CandidateMatcher
make it infeasible for a user of the Lucene Monitor library to create their own compositeCandidateMatcher
(for example, an alternate version ofParallelMatcher
) on top of existingCandidateMatcher
instances if they are outside the package.As an example, the existing implementation of
ParallelMatcher
makes use of delegateCandidateMatcher
instances, but a user would not be able to write their own version of this in their own package, becausematchQuery()
,finish()
andreportError()
all have package-private access (see this gist, comments labeled "PROBLEM").Solution
The simplest solution to this problem would be to increase the visibility of those functions in
CandidateMatcher
(either toprotected
orpublic
), but it runs the risk of violating encapsulation, so I've tried to avoid that here.The solution I've put together here introduces a
WrappedCandidateMatcher
, which can be used to increase visibility when creating a composite matcher.WrappedCandidateMatcher
does NOT extend fromCandidateMatcher
but has nearly the same interface, with increased visibility onmatchQuery()
,finish()
andreportError()
. A user building a newCandidateMatcher
by composing existingCandidateMatcher
instances could make full use of thoseCandidateMatchers
by wrapping them withWrappedCandidateMatcher
, allowing them to be used internally to the composite matcher (see this gist, comments labeled "SOLUTION").On a certain level, this solution also violates existing encapsulation, but it requires the user to jump through a few hoops to get there, so it would limit the potential for misuse of a
CandidateMatcher
. If we would instead prefer to increase the visibility onCandidateMatcher
, let me know and I can do that instead.Merge Request
If this PR gets merged, can you please use my
[email protected]
email address for the squash+merge. Thank you.