Skip to content

Commit 320d6ba

Browse files
ahornaceVladimir Kotal
authored andcommitted
Add possibility to retrieve popularity data
1 parent aa0caff commit 320d6ba

File tree

9 files changed

+220
-0
lines changed

9 files changed

+220
-0
lines changed

src/org/opensolaris/opengrok/web/api/v1/controller/SuggesterController.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,21 +46,27 @@
4646
import javax.validation.constraints.Min;
4747
import javax.ws.rs.BeanParam;
4848
import javax.ws.rs.Consumes;
49+
import javax.ws.rs.DefaultValue;
4950
import javax.ws.rs.GET;
5051
import javax.ws.rs.POST;
5152
import javax.ws.rs.Path;
53+
import javax.ws.rs.PathParam;
5254
import javax.ws.rs.Produces;
55+
import javax.ws.rs.QueryParam;
5356
import javax.ws.rs.WebApplicationException;
5457
import javax.ws.rs.core.MediaType;
5558
import javax.ws.rs.core.Response;
5659
import java.net.MalformedURLException;
5760
import java.net.URL;
5861
import java.time.Duration;
5962
import java.time.Instant;
63+
import java.util.AbstractMap.SimpleEntry;
6064
import java.util.List;
6165
import java.util.Map;
66+
import java.util.Map.Entry;
6267
import java.util.logging.Level;
6368
import java.util.logging.Logger;
69+
import java.util.stream.Collectors;
6470

6571
/**
6672
* Endpoint for suggester related REST queries.
@@ -71,6 +77,8 @@ public final class SuggesterController {
7177

7278
public static final String PATH = "suggest";
7379

80+
private static final int POPULARITY_DEFAULT_PAGE_SIZE = 100;
81+
7482
private static final Logger logger = LoggerFactory.getLogger(SuggesterController.class);
7583

7684
private final RuntimeEnvironment env = RuntimeEnvironment.getInstance();
@@ -245,6 +253,21 @@ public void addSearchCountsRaw(@Valid final List<TermIncrementData> termIncremen
245253
}
246254
}
247255

256+
@GET
257+
@Path("/popularity/{project}")
258+
@Localhost
259+
@Produces(MediaType.APPLICATION_JSON)
260+
public List<Entry<String, Integer>> getPopularityData(
261+
@PathParam("project") final String project,
262+
@QueryParam("field") @DefaultValue(QueryBuilder.FULL) final String field,
263+
@QueryParam("page") @DefaultValue("" + 0) final int page,
264+
@QueryParam("pageSize") @DefaultValue("" + POPULARITY_DEFAULT_PAGE_SIZE) final int pageSize
265+
) {
266+
return suggester.getPopularityData(project, field, page, pageSize).stream()
267+
.map(e -> new SimpleEntry<>(e.getKey().utf8ToString(), e.getValue()))
268+
.collect(Collectors.toList());
269+
}
270+
248271
private static class Result {
249272

250273
private long time;

src/org/opensolaris/opengrok/web/api/v1/suggester/provider/service/SuggesterService.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@
2424

2525
import org.apache.lucene.index.Term;
2626
import org.apache.lucene.search.Query;
27+
import org.apache.lucene.util.BytesRef;
2728
import org.opengrok.suggest.LookupResultItem;
2829
import org.opengrok.suggest.query.SuggesterQuery;
2930

3031
import java.util.Collection;
3132
import java.util.List;
33+
import java.util.Map.Entry;
3234

3335
public interface SuggesterService {
3436

@@ -75,6 +77,16 @@ public interface SuggesterService {
7577
*/
7678
void increaseSearchCount(String project, Term term, int value);
7779

80+
/**
81+
* Returns the searched terms sorted according to their popularity.
82+
* @param project project for which to return the data
83+
* @param field field for which to return the data
84+
* @param page which page of data to retrieve
85+
* @param pageSize number of results to return
86+
* @return list of terms with their popularity
87+
*/
88+
List<Entry<BytesRef, Integer>> getPopularityData(String project, String field, int page, int pageSize);
89+
7890
/**
7991
* Closes the underlying service explicitly.
8092
*/

src/org/opensolaris/opengrok/web/api/v1/suggester/provider/service/impl/SuggesterServiceImpl.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import com.cronutils.parser.CronParser;
3030
import org.apache.lucene.index.Term;
3131
import org.apache.lucene.search.Query;
32+
import org.apache.lucene.util.BytesRef;
3233
import org.opengrok.suggest.LookupResultItem;
3334
import org.opengrok.suggest.Suggester;
3435
import org.opengrok.suggest.Suggester.NamedIndexReader;
@@ -52,6 +53,7 @@
5253
import java.util.Collections;
5354
import java.util.LinkedList;
5455
import java.util.List;
56+
import java.util.Map.Entry;
5557
import java.util.Objects;
5658
import java.util.Optional;
5759
import java.util.concurrent.Executors;
@@ -238,6 +240,26 @@ public void increaseSearchCount(final String project, final Term term, final int
238240
}
239241
}
240242

243+
/** {@inheritDoc} */
244+
@Override
245+
public List<Entry<BytesRef, Integer>> getPopularityData(
246+
final String project,
247+
final String field,
248+
final int page,
249+
final int pageSize
250+
) {
251+
lock.readLock().lock();
252+
try {
253+
if (suggester == null) {
254+
logger.log(Level.FINE, "Cannot retrieve popularity data because suggester is not initialized");
255+
return Collections.emptyList();
256+
}
257+
return suggester.getSearchCounts(project, field, page, pageSize);
258+
} finally {
259+
lock.readLock().unlock();
260+
}
261+
}
262+
241263
private void initSuggester() {
242264
Configuration config = env.getConfiguration();
243265

suggester/src/main/java/org/opengrok/suggest/FieldWFSTCollection.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import java.util.HashMap;
5353
import java.util.List;
5454
import java.util.Map;
55+
import java.util.Map.Entry;
5556
import java.util.Set;
5657
import java.util.concurrent.locks.ReadWriteLock;
5758
import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -455,6 +456,28 @@ public void unlock() {
455456
lock.readLock().unlock();
456457
}
457458

459+
/**
460+
* Returns the searched terms sorted according to their popularity.
461+
* @param field field for which to return the data
462+
* @param page which page of data to retrieve
463+
* @param pageSize number of results to return
464+
* @return list of terms with their popularity
465+
*/
466+
public List<Entry<BytesRef, Integer>> getSearchCountsSorted(final String field, int page, int pageSize) {
467+
lock.readLock().lock();
468+
try {
469+
PopularityMap map = searchCountMaps.get(field);
470+
if (map == null) {
471+
logger.log(Level.FINE, "No search count map initialized for field {0}", field);
472+
return Collections.emptyList();
473+
}
474+
475+
return map.getPopularityData(page, pageSize);
476+
} finally {
477+
lock.readLock().unlock();
478+
}
479+
}
480+
458481
@Override
459482
public String toString() {
460483
return "FieldWFSTCollection{" +

suggester/src/main/java/org/opengrok/suggest/Suggester.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.apache.lucene.search.Query;
2929
import org.apache.lucene.store.Directory;
3030
import org.apache.lucene.store.FSDirectory;
31+
import org.apache.lucene.util.BytesRef;
3132
import org.opengrok.suggest.query.SuggesterPrefixQuery;
3233
import org.opengrok.suggest.query.SuggesterQuery;
3334

@@ -41,6 +42,7 @@
4142
import java.util.Collections;
4243
import java.util.List;
4344
import java.util.Map;
45+
import java.util.Map.Entry;
4446
import java.util.concurrent.ConcurrentHashMap;
4547
import java.util.concurrent.ExecutorService;
4648
import java.util.concurrent.Executors;
@@ -384,6 +386,30 @@ public void increaseSearchCount(final String project, final Term term, final int
384386
data.incrementSearchCount(term, value);
385387
}
386388

389+
/**
390+
* Returns the searched terms sorted according to their popularity.
391+
* @param project project for which to return the data
392+
* @param field field for which to return the data
393+
* @param page which page of data to retrieve
394+
* @param pageSize number of results to return
395+
* @return list of terms with their popularity
396+
*/
397+
public List<Entry<BytesRef, Integer>> getSearchCounts(
398+
final String project,
399+
final String field,
400+
final int page,
401+
final int pageSize
402+
) {
403+
FieldWFSTCollection data = projectData.get(project);
404+
if (data == null) {
405+
logger.log(Level.FINE, "Cannot retrieve search counts because data for project {0} were not found",
406+
project);
407+
return Collections.emptyList();
408+
}
409+
410+
return data.getSearchCountsSorted(field, page, pageSize);
411+
}
412+
387413
/**
388414
* Closes opened resources.
389415
*/

suggester/src/main/java/org/opengrok/suggest/popular/PopularityMap.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424

2525
import org.apache.lucene.util.BytesRef;
2626

27+
import java.util.List;
28+
import java.util.Map.Entry;
29+
2730
/**
2831
* Abstraction for the map to store the data for most popular completion.
2932
*/
@@ -36,4 +39,12 @@ public interface PopularityMap extends PopularityCounter, AutoCloseable {
3639
*/
3740
void increment(BytesRef key, int value);
3841

42+
/**
43+
* Returns the popularity data sorted according to their value.
44+
* @param page which page of data to retrieve
45+
* @param pageSize number of results to return
46+
* @return popularity data sorted according to their value
47+
*/
48+
List<Entry<BytesRef, Integer>> getPopularityData(int page, int pageSize);
49+
3950
}

suggester/src/main/java/org/opengrok/suggest/popular/impl/chronicle/ChronicleMapAdapter.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030
import java.io.IOException;
3131
import java.nio.file.Files;
3232
import java.nio.file.Path;
33+
import java.util.ArrayList;
34+
import java.util.Collections;
35+
import java.util.List;
36+
import java.util.Map.Entry;
3337
import java.util.function.Predicate;
3438

3539
/**
@@ -67,6 +71,31 @@ public void increment(final BytesRef key, final int value) {
6771
map.merge(key, value, (a, b) -> a + b);
6872
}
6973

74+
/** {@inheritDoc} */
75+
@Override
76+
public List<Entry<BytesRef, Integer>> getPopularityData(final int page, final int pageSize) {
77+
if (page < 0) {
78+
throw new IllegalArgumentException("Cannot retrieve popularity data for negative page: " + page);
79+
}
80+
if (pageSize < 0) {
81+
throw new IllegalArgumentException("Cannot retrieve negative number of results: " + pageSize);
82+
}
83+
84+
List<Entry<BytesRef, Integer>> list = new ArrayList<>(map.entrySet());
85+
list.sort(Entry.<BytesRef, Integer>comparingByValue().reversed());
86+
87+
int startIndex = page * pageSize;
88+
if (startIndex >= list.size()) {
89+
return Collections.emptyList();
90+
}
91+
int endIndex = startIndex + pageSize;
92+
if (endIndex > list.size()) {
93+
endIndex = list.size();
94+
}
95+
96+
return list.subList(startIndex, endIndex);
97+
}
98+
7099
/**
71100
* Removes the entries with key that meets the predicate.
72101
* @param predicate predicate which tests which entries should be removed

suggester/src/test/java/org/opengrok/suggest/popular/impl/ChronicleMapAdapterTest.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,13 @@
3131
import java.io.IOException;
3232
import java.nio.file.Files;
3333
import java.nio.file.Path;
34+
import java.util.AbstractMap.SimpleEntry;
35+
import java.util.List;
36+
import java.util.Map.Entry;
3437

38+
import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
3539
import static org.junit.Assert.assertEquals;
40+
import static org.junit.Assert.assertThat;
3641

3742
public class ChronicleMapAdapterTest {
3843

@@ -86,4 +91,36 @@ public void testResize() throws IOException {
8691
checkData(500, map);
8792
}
8893

94+
@Test
95+
public void testGetPopularityData() {
96+
Entry<BytesRef, Integer> e1 = new SimpleEntry<>(new BytesRef("test"), 1);
97+
Entry<BytesRef, Integer> e2 = new SimpleEntry<>(new BytesRef("test2"), 2);
98+
99+
map.increment(e1.getKey(), e1.getValue());
100+
map.increment(e2.getKey(), e2.getValue());
101+
102+
List<Entry<BytesRef, Integer>> data = map.getPopularityData(0, 10);
103+
104+
assertThat(data, contains(e2, e1));
105+
}
106+
107+
@Test
108+
public void testGetPopularityPaging() {
109+
Entry<BytesRef, Integer> e1 = new SimpleEntry<>(new BytesRef("test"), 1);
110+
Entry<BytesRef, Integer> e2 = new SimpleEntry<>(new BytesRef("test2"), 2);
111+
Entry<BytesRef, Integer> e3 = new SimpleEntry<>(new BytesRef("test3"), 3);
112+
113+
map.increment(e1.getKey(), e1.getValue());
114+
map.increment(e2.getKey(), e2.getValue());
115+
map.increment(e3.getKey(), e3.getValue());
116+
117+
List<Entry<BytesRef, Integer>> data = map.getPopularityData(0, 2);
118+
119+
assertThat(data, contains(e3, e2));
120+
121+
data = map.getPopularityData(1, 2);
122+
123+
assertThat(data, contains(e1));
124+
}
125+
89126
}

0 commit comments

Comments
 (0)