Skip to content
This repository was archived by the owner on Sep 19, 2023. It is now read-only.

Commit 82626ed

Browse files
DATASOLR-196 - Introduce connect() and notOperator() to Criteria.
connect() allows to explicitly create bracketing. //z:roo AND (x:foo OR y:bar) where(“z”).is(“roo”).connect().and(where(“x”).is(“foo”).or(“y”).is(“bar”)) notOpereator allows to wrap whole or expression parts inside a negating statement //z:roo OR -(x:foo OR y:bar) where("z").is("roo").connect().or(where("x").is("foo").or("y").is("bar").notOperator())
1 parent 225f52f commit 82626ed

File tree

5 files changed

+172
-5
lines changed

5 files changed

+172
-5
lines changed

src/main/java/org/springframework/data/solr/core/QueryParserBase.java

+6-2
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,12 @@ public String createQueryStringFromNode(Node node, int position) {
139139
if (position > 0) {
140140
query.append(node.isOr() ? " OR " : " AND ");
141141
}
142+
142143
if (node.hasSiblings()) {
143-
if (!node.isRoot()) {
144+
if (node.isNegating()) {
145+
query.append("-");
146+
}
147+
if (!node.isRoot() || (node.isRoot() && node.isNegating())) {
144148
query.append('(');
145149
}
146150

@@ -149,7 +153,7 @@ public String createQueryStringFromNode(Node node, int position) {
149153
query.append(createQueryStringFromNode(nested, i++));
150154
}
151155

152-
if (!node.isRoot()) {
156+
if (!node.isRoot() || (node.isRoot() && node.isNegating())) {
153157
query.append(')');
154158
}
155159
} else {

src/main/java/org/springframework/data/solr/core/query/Criteria.java

+29-3
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ public class Criteria extends Node {
4444

4545
private Field field;
4646
private float boost = Float.NaN;
47-
private boolean negating = false;
4847

4948
private Set<Predicate> predicates = new LinkedHashSet<Predicate>();
5049

@@ -302,10 +301,25 @@ public Criteria endsWith(Iterable<String> values) {
302301
* @return
303302
*/
304303
public Criteria not() {
305-
this.negating = true;
304+
setNegating(true);
306305
return this;
307306
}
308307

308+
/**
309+
* Explicitly wrap {@link Criteria} inside not operation.
310+
*
311+
* @since 1.4
312+
* @return
313+
*/
314+
public Criteria notOperator() {
315+
316+
Crotch c = new Crotch();
317+
c.setNegating(true);
318+
c.add(this);
319+
320+
return c;
321+
}
322+
309323
/**
310324
* Crates new {@link Predicate} with trailing {@code ~}
311325
*
@@ -574,7 +588,7 @@ public Field getField() {
574588
* @return true if {@code not()} criteria
575589
*/
576590
public boolean isNegating() {
577-
return this.negating;
591+
return super.isNegating();
578592
}
579593

580594
/**
@@ -709,6 +723,18 @@ public String toString() {
709723

710724
// -------- NODE STUFF ---------
711725

726+
/**
727+
* Explicitly connect {@link Criteria} with another one allows to create explicit bracketing.
728+
*
729+
* @since 1.4
730+
* @return
731+
*/
732+
public Criteria connect() {
733+
Crotch c = new Crotch();
734+
c.add(this);
735+
return c;
736+
}
737+
712738
@SuppressWarnings("unchecked")
713739
@Override
714740
public Crotch and(Node node) {

src/main/java/org/springframework/data/solr/core/query/Crotch.java

+12
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,22 @@ public Crotch boost(float boost) {
5757

5858
@Override
5959
public Crotch not() {
60+
6061
mostRecentSibling.not();
6162
return this;
6263
}
6364

65+
@Override
66+
public Crotch notOperator() {
67+
68+
if (this.isRoot()) {
69+
this.setNegating(true);
70+
} else {
71+
super.notOperator();
72+
}
73+
return this;
74+
}
75+
6476
@Override
6577
public Crotch endsWith(String postfix) {
6678
mostRecentSibling.endsWith(postfix);

src/main/java/org/springframework/data/solr/core/query/Node.java

+17
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public abstract class Node {
3030

3131
private Node parent;
3232
private boolean isOr = false;
33+
private boolean negating = false;
3334

3435
protected Node() {}
3536

@@ -84,6 +85,22 @@ public Collection<Criteria> getSiblings() {
8485
return Collections.emptyList();
8586
}
8687

88+
/**
89+
* @return true if {@code not()} criteria
90+
* @since 1.4
91+
*/
92+
public boolean isNegating() {
93+
return this.negating;
94+
}
95+
96+
/**
97+
* @param negating
98+
* @since 1.4
99+
*/
100+
protected void setNegating(boolean negating) {
101+
this.negating = negating;
102+
}
103+
87104
// ------ CONJUNCTIONS --------
88105
/**
89106
* Combine two {@link Node}s using {@literal and}.

src/test/java/org/springframework/data/solr/core/DefaultQueryParserTests.java

+108
Original file line numberDiff line numberDiff line change
@@ -1101,6 +1101,114 @@ public void testConstructGroupQueryWithMultipleQueries() {
11011101
Assert.assertNull(solrQuery.getParams(GroupParams.GROUP_FIELD));
11021102
}
11031103

1104+
/**
1105+
* @see DATASOLR-196
1106+
*/
1107+
@Test
1108+
public void connectShouldAllowConcatinationOfCriteriaWithAndPreservingDesiredBracketing() {
1109+
1110+
Criteria part1 = Criteria.where("z").is("roo");
1111+
Criteria part2 = Criteria.where("x").is("foo").or("y").is("bar");
1112+
Criteria criteria = part1.connect().and(part2);
1113+
1114+
Assert.assertEquals("z:roo AND (x:foo OR y:bar)", queryParser.createQueryStringFromNode(criteria));
1115+
}
1116+
1117+
/**
1118+
* @see DATASOLR-196
1119+
*/
1120+
@Test
1121+
public void connectShouldAllowConcatinationOfCriteriaWithAndPreservingDesiredBracketingReverse() {
1122+
1123+
Criteria part1 = Criteria.where("z").is("roo");
1124+
Criteria part2 = Criteria.where("x").is("foo").or("y").is("bar");
1125+
Criteria criteria = part2.connect().and(part1);
1126+
1127+
Assert.assertEquals("(x:foo OR y:bar) AND z:roo", queryParser.createQueryStringFromNode(criteria));
1128+
}
1129+
1130+
/**
1131+
* @see DATASOLR-196
1132+
*/
1133+
@Test
1134+
public void connectShouldAllowConcatinationOfCriteriaWithOrPreservingDesiredBracketing() {
1135+
1136+
Criteria part1 = Criteria.where("z").is("roo");
1137+
Criteria part2 = Criteria.where("x").is("foo").or("y").is("bar");
1138+
Criteria criteria = part1.connect().or(part2);
1139+
1140+
Assert.assertEquals("z:roo OR (x:foo OR y:bar)", queryParser.createQueryStringFromNode(criteria));
1141+
}
1142+
1143+
/**
1144+
* @see DATASOLR-196
1145+
*/
1146+
@Test
1147+
public void connectShouldAllowConcatinationOfCriteriaWithOrPreservingDesiredBracketingReverse() {
1148+
1149+
Criteria part1 = Criteria.where("z").is("roo");
1150+
Criteria part2 = Criteria.where("x").is("foo").or("y").is("bar");
1151+
Criteria criteria = part2.connect().or(part1);
1152+
1153+
Assert.assertEquals("(x:foo OR y:bar) OR z:roo", queryParser.createQueryStringFromNode(criteria));
1154+
}
1155+
1156+
/**
1157+
* @see DATASOLR-196
1158+
*/
1159+
@Test
1160+
public void notOperatorShouldWrapWholeExpression() {
1161+
1162+
Criteria part1 = Criteria.where("text").startsWith("fx").or("product_code").startsWith("fx");
1163+
Criteria part2 = Criteria.where("text").startsWith("option").or("product_code").startsWith("option");
1164+
Criteria criteria = part1.connect().and(part2).notOperator();
1165+
1166+
String expected = "-((text:fx* OR product_code:fx*) AND (text:option* OR product_code:option*))";
1167+
Assert.assertEquals(expected, queryParser.createQueryStringFromNode(criteria));
1168+
}
1169+
1170+
/**
1171+
* @see DATASOLR-196
1172+
*/
1173+
@Test
1174+
public void notOperatorShouldWrapNestedExpressionCorrectly() {
1175+
1176+
Criteria part1 = Criteria.where("z").is("roo");
1177+
Criteria part2 = Criteria.where("x").is("foo").or("y").is("bar").notOperator();
1178+
1179+
Criteria criteria = part1.connect().or(part2);
1180+
1181+
Assert.assertEquals("z:roo OR -(x:foo OR y:bar)", queryParser.createQueryStringFromNode(criteria));
1182+
}
1183+
1184+
/**
1185+
* @see DATASOLR-196
1186+
*/
1187+
@Test
1188+
public void notOperatorShouldWrapNestedExpressionCorrectlyReverse() {
1189+
1190+
Criteria part1 = Criteria.where("z").is("roo");
1191+
Criteria part2 = Criteria.where("x").is("foo").or("y").is("bar").notOperator();
1192+
1193+
Criteria criteria = part2.connect().or(part1);
1194+
1195+
Assert.assertEquals("-(x:foo OR y:bar) OR z:roo", queryParser.createQueryStringFromNode(criteria));
1196+
}
1197+
1198+
/**
1199+
* @see DATASOLR-196
1200+
*/
1201+
@Test
1202+
public void notOperatorShouldWrapNestedExpressionCorrectlyReverseWithDoubleNegation() {
1203+
1204+
Criteria part1 = Criteria.where("z").is("roo");
1205+
Criteria part2 = Criteria.where("x").is("foo").or("y").is("bar").notOperator();
1206+
1207+
Criteria criteria = part2.connect().and(part1).notOperator();
1208+
1209+
Assert.assertEquals("-(-(x:foo OR y:bar) AND z:roo)", queryParser.createQueryStringFromNode(criteria));
1210+
}
1211+
11041212
private void assertPivotFactingPresent(SolrQuery solrQuery, String... expected) {
11051213
Assert.assertArrayEquals(expected, solrQuery.getParams(FacetParams.FACET_PIVOT));
11061214
}

0 commit comments

Comments
 (0)