Your feed back is greatly appreciated. The implementation that I have is the result of my very specific use cases applied over several years. My use cases may be so specialized that this isn't meaningful outside of my domain. And that's okay.
I have use cases where a search request is converted to a Solr Boolean query. Then the results of that conversion may be altered in many ways. Suppose the weights and scores are to be altered because of special ranking model is applied. Here is a very simplified and contrived example of some of the things I deal with in generating queries. public TermGroup generate(SearchRequest request) { TermGroup group = new TermGroup().withLabel("FULL_REQUEST"); TermGroup groupA = new TermGroup().withLabel("FIRST_NAMES"); group.addGroup(groupA); groupA.addTerm(new Term("firstName", "jeff")); groupA.addTerm(new Term("firstName", "jeffrey")); groupA.addTerm(new Term("firstName", "geoffrey")); TermGroup groupB = new TermGroup().withLabel("MIDDLE_NAMES"); group.addGroup(groupB); groupB.addTerm(new Term("middleName", "john")); groupB.addTerm(new Term("middleName", "jon")); groupB.addTerm(new Term("middleName", "sean")); TermGroup groupC = new TermGroup().withLabel("LAST_NAMES"); group.addGroup(groupC); groupC.addTerm(new Term("lastName", "smith")); groupC.addTerm(new Term("lastName", "smythe")); groupC.addTerm(new Term("lastName", "schmidt")); return group; } public void contrivedExample() { SearchRequest request = new SearchRequest(); TermGroup requestGroup = generate(request); String prettyQuery = requestGroup.prettyPrint(true, "", " ", "\n"); //At least one match on the middle names is wanted List<TermGroup> middleNames = requestGroup.findByLabel("MIDDLE_NAMES"); middleNames.get(0).setOccur(Occur.MUST); prettyQuery = requestGroup.prettyPrint(true, "", " ", "\n"); //We want to boost last names List<TermGroup> lastNames = requestGroup.findByLabel("LAST_NAMES"); lastNames.get(0).setBoost(2.0f); prettyQuery = requestGroup.prettyPrint(true, "", " ", "\n"); //We want to weight the first names List<TermGroup> firstNames = requestGroup.findByLabel("FIRST_NAMES"); firstNames.get(0).setBoost( 1.0f / firstNames.get(0).getTerms().size()); prettyQuery = requestGroup.prettyPrint(true, "", " ", "\n"); } Here are the "prettyQuery" in order of the code: The original: /* FULL_REQUEST */ ( /* FIRST_NAMES */ ( firstName:jeff firstName:jeffrey firstName:geoffrey ) /* MIDDLE_NAMES */ ( middleName:john middleName:jon middleName:sean ) /* LASTNAMES */ ( lastName:smith lastName:smythe lastName:schmidt ) ) Must have aleast one match on Middle Name: /* FULL_REQUEST */ ( /* FIRST_NAMES */ ( firstName:jeff firstName:jeffrey firstName:geoffrey ) /* MIDDLE_NAMES */ +( middleName:john middleName:jon middleName:sean ) /* LASTNAMES */ ( lastName:smith lastName:smythe lastName:schmidt ) ) //Boost the score of the Last Names /* FULL_REQUEST */ ( /* FIRST_NAMES */ ( firstName:jeff firstName:jeffrey firstName:geoffrey ) /* MIDDLE_NAMES */ +( middleName:john middleName:jon middleName:sean ) /* LAST_NAMES */ ( lastName:smith lastName:smythe lastName:schmidt )^2 ) Add a weight to the first names: /* FULL_REQUEST */ ( /* FIRST_NAMES */ ( firstName:jeff firstName:jeffrey firstName:geoffrey )^0.3333 /* MIDDLE_NAMES */ +( middleName:john middleName:jon middleName:sean ) /* LAST_NAMES */ ( lastName:smith lastName:smythe lastName:schmidt )^2 ) Sometimes I add more terms. Sometimes I remove a group (sub query). Sometimes I surround a group inside another group and then add sibling groups to that group. I have generators that generate all types of groups (sub queries) and then I arrange them and place modifiers on them in a miriad of ways. The symantics of using a Builder pattern, or a fluent pattern, or just getters and setters are implementation details. The style of how things are done in Solrj should previal if this is ever added to SolrJ. The use cases I have dealt with that caused me to create this type of code is based on complex query generation. Often I do not know how many Terms will be in the TermGroup because of the request. After I generate the group I might apply a boost like this: firstNameGroup.setBoost( 1.0f / firstNameGroup.getTerms().size()); I might have a FristNameQueryGenerator, MiddleNameQUeryGenerator, and LastNameQuerGenerator. Then I might have a FullNameQueryGenerator that encapulates the first, middle, and last name genrators and place them all in a group with custom modifiers to get the score and ranking I desire. I agree with your advice about growing attached to an implementation. My broader implementation inside of our proprietary code I am completely attached to and will not be moving from it. I just pulled out part of that implementation to share. I have only put in about 6 hours of my time to date on sharing this, and I do not feel it is time ill used. Again, thanks for your time and thoughtful responses. Geoffrey > On Aug 20, 2024, at 10:07 AM, David Smiley <dsmi...@apache.org> wrote: > > Let's bikeshed before you write code, okay? Otherwise you potentially > waste time and/or grow attached to sunk costs. > > Feedback: > * avoid the word "term"; it already has Lucene definition and a Solr > query parser but you're using it in a way that isn't either. I > recommend simply for "fieldQuery" -- these queries target specific > fields after all. > * Can we avoid top level classes that the user must know about; > instead having one class -- QueryBuilder (or named QueryStringBuilder) > with factory methods that are easily discoverable? Not a huge deal. > * Instead of "Group", lets acknowledge these map to a BooleanQuery so > I think "bool" in some way should be used instead. Some bool builder > can then have must() should() filter() methods without needing an > enum. > * Can't import any Lucene things > > I'll add examples below of my feedback ideas. > > On Tue, Aug 20, 2024 at 11:04 AM Geoffrey Slinker > <geoffrey_slin...@yahoo.com.invalid> wrote: > >> Instantiate a Term and set the values and call toString to get a string that >> can be used in a Standard Solr Query. >> Term term = new Term("pink panther").withBoost(1.5f); >> term. toString() >> >> Output: "pink panther"^1.5 >> >> Term term = new Term("title", "pink panther").withBoost(1.5f); >> term. toString() >> >> Output: title:"pink panther"^1.5 > > final QueryStringBuilder B = new QueryStringBuilder(potential > options); // immutable > B.field("title", "ping panther").withBoost(1.5f).toString(); > > >> TermGroup group = new TermGroup().with(Occur. MUST).withBoost(1.4f); >> group. addTerm(new Term("foo", "bar").withProximity(1)); >> >> String query = group. toString(); >> >> Output: +( foo:bar~1 )^1.4 > > the outer MUST is pointless but I'll recreate anyway: > > final QueryStringBuilder B = new QueryStringBuilder(potential > options); // immutable > B.bool().must(B.fieldFuzzy("foo", "bar", 1).withBoost(1.4)).toString(); > >> Example: >> TermGroup group = new TermGroup().withConstantScore(5.0f); >> group. addTerm(new Term("foo", "bar").withProximity(1)); >> >> String query = group. toString(); >> >> Output: ( foo:bar~1 )^=5 > > final QueryStringBuilder B = new QueryStringBuilder(potential > options); // immutable > B.fieldFuzzy("foo", "bar", 1).withConstantScore(5.0f).toString(); > // no "group" terminology necessary > >> Instead of using string manipulation to create complex query strings the >> TermGroup allows complex queries to be built inside an object model that can >> be more easily changed. >> If you need to generate a query like this: >> +( >> ( >> title:"Grand Illusion"~1 >> title:"Paradise Theatre"~1 >> )^0.3 >> ( >> title:"Night At The Opera"~1 >> title:"News Of The World"~1 >> )^0.3 >> ( >> title:"Van Halen"~1 >> title:1984~1 >> )^0.3 >> ) >> >> >> The code to do so is as simple this: >> >> TermGroup group = new TermGroup().with(Occur. MUST); >> >> TermGroup favoriteStyx = group. addGroup().withBoost(0.3f); >> TermGroup favoriteQueen = group. addGroup().withBoost(0.3f); >> TermGroup favoriteVanHalen = group. addGroup().withBoost(0.3f); >> >> favoriteStyx. addTerm(new Term("title","Grand Illusion").with(Occur. >> SHOULD).withProximity(1)); >> favoriteStyx. addTerm(new Term("title","Paradise Theatre").with(Occur. >> SHOULD).withProximity(1)); >> >> favoriteQueen. addTerm(new Term("title","Night At The >> Opera").with(Occur. SHOULD).withProximity(1)); >> favoriteQueen. addTerm(new Term("title","News Of The >> World").with(Occur. SHOULD).withProximity(1)); >> >> favoriteVanHalen. addTerm(new Term("title","Van Halen").with(Occur. >> SHOULD).withProximity(1)); >> favoriteVanHalen. addTerm(new Term("title","1984").with(Occur. >> SHOULD).withProximity(1)); >> > > // again, the outer bool MUST is pointless but will recreate your example > > final QueryStringBuilder B = new QueryStringBuilder(potential > options); // immutable > > var favoriteStyx = B.bool(); > favoriteStyx.should(B.field("title", "Grand Illusion").withProximity(1)); > favoriteStyx.should(B.field("title", "Paradise Theater").withProximity(1)); > > var favoriteQueen = B.bool(); > favoriteQueen.should(B.field("title", "Night At The Opera").withProximity(1)); > favoriteQueen.should(B.field("title", "News Of The World").withProximity(1)); > > var favoriteVanHalen = B.bool(); > favoriteVanHalen.should(B.field("title", "Van Halen").withProximity(1)); > favoriteVanHalen.should(B.field("title", "1984").withProximity(1)); > > B.bool().must( // pointless wrap > B.bool().should(favoriteStyx.withBoost(0.3f)) > .should(favoriteQueen.withBoost(0.3f)) > .should(favoriteVanHalen.withBoost(0.3f)) > ).toString(); > > --- > If we imagine plausibly expanding support to write Solr JSON as an > alternative, then it could affect the code choices. Like > toSolrLuceneSyntax() and toSolrQueryDsl(). > > --------------------------------------------------------------------- > To unsubscribe, e-mail: dev-unsubscr...@solr.apache.org > For additional commands, e-mail: dev-h...@solr.apache.org > --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@solr.apache.org For additional commands, e-mail: dev-h...@solr.apache.org