1+ package com .redis .om .spring .fixtures .document .repository ;
2+
3+ import static org .assertj .core .api .Assertions .assertThat ;
4+ import static org .junit .jupiter .api .Assertions .*;
5+
6+ import java .util .Arrays ;
7+ import java .util .List ;
8+ import java .util .stream .Collectors ;
9+
10+ import org .junit .jupiter .api .AfterEach ;
11+ import org .junit .jupiter .api .BeforeEach ;
12+ import org .junit .jupiter .api .Test ;
13+ import org .springframework .beans .factory .annotation .Autowired ;
14+
15+ import com .redis .om .spring .AbstractBaseDocumentTest ;
16+ import com .redis .om .spring .fixtures .document .model .NumericArrayTestData ;
17+ import com .redis .om .spring .fixtures .document .model .NumericArrayTestData$ ;
18+ import com .redis .om .spring .search .stream .EntityStream ;
19+
20+ /**
21+ * Test class to demonstrate the issue described in GitHub issue #400:
22+ * https://github.com/redis/redis-om-spring/issues/400
23+ *
24+ * The issue is that NumericField lacks methods to check if a numeric array
25+ * contains specific numbers when using EntityStream, similar to how TagField.in() works.
26+ *
27+ * This test proves that the functionality is missing and shows what should work
28+ * once the feature is implemented.
29+ */
30+ class NumericArraySearchTest extends AbstractBaseDocumentTest {
31+
32+ @ Autowired
33+ NumericArrayTestDataRepository repository ;
34+
35+ @ Autowired
36+ EntityStream entityStream ;
37+
38+ private NumericArrayTestData testData1 ;
39+ private NumericArrayTestData testData2 ;
40+ private NumericArrayTestData testData3 ;
41+
42+ @ BeforeEach
43+ void loadTestData () {
44+ testData1 = new NumericArrayTestData ();
45+ testData1 .setName ("data1" );
46+ testData1 .setMeasurements (Arrays .asList (1.5 , 2.5 , 3.5 ));
47+ testData1 .setCounts (Arrays .asList (10L , 20L , 30L ));
48+ testData1 .setRatings (Arrays .asList (1 , 2 , 3 ));
49+ testData1 .setTags (Arrays .asList ("tag1" , "tag2" ));
50+
51+ testData2 = new NumericArrayTestData ();
52+ testData2 .setName ("data2" );
53+ testData2 .setMeasurements (Arrays .asList (2.5 , 4.5 , 6.5 ));
54+ testData2 .setCounts (Arrays .asList (20L , 40L , 60L ));
55+ testData2 .setRatings (Arrays .asList (2 , 4 , 6 ));
56+ testData2 .setTags (Arrays .asList ("tag2" , "tag3" ));
57+
58+ testData3 = new NumericArrayTestData ();
59+ testData3 .setName ("data3" );
60+ testData3 .setMeasurements (Arrays .asList (3.5 , 7.5 , 9.5 ));
61+ testData3 .setCounts (Arrays .asList (30L , 70L , 90L ));
62+ testData3 .setRatings (Arrays .asList (3 , 7 , 9 ));
63+ testData3 .setTags (Arrays .asList ("tag1" , "tag4" ));
64+
65+ repository .saveAll (Arrays .asList (testData1 , testData2 , testData3 ));
66+ }
67+
68+ @ AfterEach
69+ void cleanUp () {
70+ repository .deleteAll ();
71+ }
72+
73+ /**
74+ * This test shows that TagField.in() works perfectly for string arrays with EntityStream.
75+ * This is the pattern that NumericField should follow for numeric arrays.
76+ */
77+ @ Test
78+ void testTagFieldInWorksWithEntityStream () {
79+ // This demonstrates TagField.in() working with EntityStream:
80+ List <NumericArrayTestData > results = entityStream
81+ .of (NumericArrayTestData .class )
82+ .filter (NumericArrayTestData$ .TAGS .in ("tag1" , "tag3" ))
83+ .collect (Collectors .toList ());
84+
85+ // Verify the results - should find testData1 and testData3 (both have "tag1") and testData2 (has "tag3")
86+ assertThat (results ).hasSize (3 );
87+ assertThat (results ).contains (testData1 , testData2 , testData3 );
88+ }
89+
90+ /**
91+ * This test demonstrates the MISSING functionality for numeric arrays.
92+ * This is the core issue from GitHub #400.
93+ */
94+ @ Test
95+ void testNumericFieldLacksContainsFunctionalityWithEntityStream () {
96+ // Test basic data setup first
97+ assertThat (testData1 .getMeasurements ()).contains (2.5 );
98+ assertThat (testData2 .getMeasurements ()).contains (2.5 );
99+ assertThat (testData3 .getMeasurements ()).contains (7.5 );
100+
101+ // Should be able to find entities where measurements contains any of these values
102+ List <NumericArrayTestData > measurementResults = entityStream
103+ .of (NumericArrayTestData .class )
104+ .filter (NumericArrayTestData$ .MEASUREMENTS .containsDouble (2.5 , 7.5 ))
105+ .collect (Collectors .toList ());
106+ assertThat (measurementResults ).hasSize (3 ); // All entities have at least one of these values
107+
108+ // Should be able to find entities where counts contains any of these values
109+ List <NumericArrayTestData > countResults = entityStream
110+ .of (NumericArrayTestData .class )
111+ .filter (NumericArrayTestData$ .COUNTS .containsLong (20L , 70L ))
112+ .collect (Collectors .toList ());
113+ assertThat (countResults ).hasSize (3 ); // All entities have at least one of these values
114+
115+ // Should be able to find entities where ratings contains any of these values
116+ List <NumericArrayTestData > ratingResults = entityStream
117+ .of (NumericArrayTestData .class )
118+ .filter (NumericArrayTestData$ .RATINGS .containsInt (2 , 7 ))
119+ .collect (Collectors .toList ());
120+ assertThat (ratingResults ).hasSize (3 ); // testData1 has 2, testData2 has 2, testData3 has 7
121+ }
122+
123+ /**
124+ * This test shows what currently happens when you try to use the existing in() method
125+ * on NumericField with EntityStream - it doesn't work as expected for arrays.
126+ */
127+ @ Test
128+ void testCurrentNumericFieldInMethodLimitation () {
129+ // The current in() method on NumericField works for scalar matching, not array membership
130+ // This is different from TagField.in() which works for array membership
131+
132+ // The current NumericField.in() method expects List<T> not individual values
133+ // This shows the fundamental difference from TagField.in() which accepts varargs
134+
135+ // This would fail to compile: TestData$.MEASUREMENTS.in(2.5)
136+ // because NumericField.in() expects List<Double>, not double
137+
138+ // NumericField.in() signature: in(List<Double> values)
139+ // TagField.in() signature: in(String... values) or in(Object... values)
140+
141+ // The current NumericField.in() method would require the metamodel:
142+ // List<NumericArrayTestData> results = entityStream
143+ // .of(NumericArrayTestData.class)
144+ // .filter(NumericArrayTestData$.MEASUREMENTS.in(Arrays.asList(2.5))) // Must pass as List
145+ // .collect(Collectors.toList());
146+
147+ // Demonstrate the data exists but we can't query it effectively
148+ assertThat (testData1 .getMeasurements ()).contains (2.5 );
149+ assertThat (testData2 .getMeasurements ()).contains (2.5 );
150+
151+ // The issue: no easy way to find entities where numeric array contains any of multiple values
152+
153+ // But even if this worked, it doesn't give us "array contains any of these values" semantics
154+ // It's checking if the field value equals the provided list, not membership
155+ assertTrue (true , "Current NumericField.in() doesn't support array membership like TagField.in() does" );
156+
157+ // The key issue: NumericField.in() ≠ TagField.in() for array membership behavior
158+ assertTrue (true , "NumericField.in() doesn't support array membership like TagField.in() does" );
159+ }
160+
161+ /**
162+ * This test demonstrates the exact scenario from GitHub issue #400.
163+ * User wants to query: "Find all entities where numeric array contains any of these values"
164+ */
165+ @ Test
166+ void testGitHubIssue400Scenario () {
167+ // Issue #400 specifically asks for functionality like:
168+ // field.containsLong(Long... values) similar to TagField.in()
169+
170+ // Verify test data setup
171+ assertThat (testData1 .getCounts ()).contains (20L );
172+ assertThat (testData2 .getCounts ()).contains (20L );
173+ assertThat (testData3 .getCounts ()).contains (70L );
174+
175+ // This should now work with the new containsLong method:
176+ List <NumericArrayTestData > results = entityStream
177+ .of (NumericArrayTestData .class )
178+ .filter (NumericArrayTestData$ .COUNTS .containsLong (20L , 70L ))
179+ .collect (Collectors .toList ());
180+
181+ // Should find testData1 (has 20L), testData2 (has 20L), and testData3 (has 70L)
182+ assertThat (results ).hasSize (3 );
183+ assertThat (results ).contains (testData1 , testData2 , testData3 );
184+ }
185+
186+ /**
187+ * This test proves the issue exists by showing the compilation failure.
188+ * When the feature is implemented, this test should be updated to verify the functionality works.
189+ */
190+ @ Test
191+ void testCompilationFailureProvesIssueExists () {
192+ // Verify test data has the expected values
193+ assertThat (testData1 .getMeasurements ()).containsAnyOf (1.5 , 4.5 , 7.5 );
194+ assertThat (testData1 .getCounts ()).containsAnyOf (10L , 40L , 70L );
195+ assertThat (testData1 .getRatings ()).containsAnyOf (1 , 4 , 7 );
196+
197+ // Now these methods should work and compile successfully:
198+
199+ List <NumericArrayTestData > measurementResults = entityStream
200+ .of (NumericArrayTestData .class )
201+ .filter (NumericArrayTestData$ .MEASUREMENTS .containsDouble (1.5 , 4.5 , 7.5 ))
202+ .collect (Collectors .toList ());
203+
204+ List <NumericArrayTestData > countResults = entityStream
205+ .of (NumericArrayTestData .class )
206+ .filter (NumericArrayTestData$ .COUNTS .containsLong (10L , 40L , 70L ))
207+ .collect (Collectors .toList ());
208+
209+ List <NumericArrayTestData > ratingResults = entityStream
210+ .of (NumericArrayTestData .class )
211+ .filter (NumericArrayTestData$ .RATINGS .containsInt (1 , 4 , 7 ))
212+ .collect (Collectors .toList ());
213+
214+ // Verify the methods work as expected
215+ assertThat (measurementResults ).hasSize (3 ); // All entities have at least one of these measurements
216+ assertThat (countResults ).hasSize (3 ); // All entities have at least one of these counts
217+ assertThat (ratingResults ).hasSize (3 ); // All entities have at least one of these ratings
218+ }
219+ }
0 commit comments