Skip to content

Commit

Permalink
Merge pull request #1045 from alex0x08/master
Browse files Browse the repository at this point in the history
"Click to search on date" and fix for reactions issue
  • Loading branch information
maxcom authored Nov 3, 2023
2 parents f240712 + d78798d commit c002379
Show file tree
Hide file tree
Showing 10 changed files with 97 additions and 25 deletions.
8 changes: 6 additions & 2 deletions src/main/java/ru/org/linux/search/SearchController.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import com.sksamuel.elastic4s.requests.searches.aggs.responses.FilterAggregationResult;
import com.sksamuel.elastic4s.requests.searches.aggs.responses.bucket.TermBucket;
import com.sksamuel.elastic4s.requests.searches.aggs.responses.bucket.Terms;
import org.joda.time.DateTimeZone;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
Expand Down Expand Up @@ -52,6 +53,7 @@
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import javax.servlet.http.HttpServletRequest;

@Controller
public class SearchController {
Expand Down Expand Up @@ -107,7 +109,8 @@ public static Map<SearchRange, String> getRanges() {
public String search(
Model model,
@ModelAttribute("query") SearchRequest query,
BindingResult bindingResult
BindingResult bindingResult,
HttpServletRequest request
) {
Map<String, Object> params = model.asMap();

Expand All @@ -116,7 +119,8 @@ public String search(

SearchViewer sv = new SearchViewer(query, client);

SearchResponse response = sv.performSearch();
final DateTimeZone tz = (DateTimeZone)request.getAttribute("timezone");
SearchResponse response = sv.performSearch(tz);

long current = System.currentTimeMillis();

Expand Down
43 changes: 39 additions & 4 deletions src/main/java/ru/org/linux/search/SearchRequest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 1998-2016 Linux.org.ru
* Copyright 1998-2023 Linux.org.ru
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
Expand All @@ -15,12 +15,15 @@

package ru.org.linux.search;

import org.joda.time.DateTimeZone;
import ru.org.linux.search.SearchEnums.SearchInterval;
import ru.org.linux.search.SearchEnums.SearchRange;
import ru.org.linux.user.User;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
Expand All @@ -35,7 +38,9 @@ public class SearchRequest {
private SearchInterval interval = SearchInterval.ALL;
private SearchRange range = SearchRange.ALL;
private int offset = 0;

private long dt;
public long getDt() { return dt; }
public void setDt(long dt) { this.dt = dt; }
public String getQ() {
return q;
}
Expand All @@ -45,7 +50,7 @@ public void setQ(String q) {
}

public boolean isInitial() {
return q.isEmpty() && user==null;
return q.isEmpty() && user==null && !isDateSelected();
}

public boolean isUsertopic() {
Expand Down Expand Up @@ -155,11 +160,41 @@ public String getQuery(int newOffset) {
return buildParams(params);
}

public boolean isDateSelected() {
return dt >0;
}

public long atEndOfDaySelected(DateTimeZone tz) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date(dt));
if (tz!=null) {
calendar.setTimeZone(tz.toTimeZone());
}
calendar.set(Calendar.HOUR_OF_DAY, 23);
calendar.set(Calendar.MINUTE, 59);
calendar.set(Calendar.SECOND, 59);
calendar.set(Calendar.MILLISECOND, 999);
return calendar.getTime().getTime();
}

public long atStartOfDaySelected(DateTimeZone tz) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date(dt));
if (tz!=null) {
calendar.setTimeZone(tz.toTimeZone());
}
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar.getTime().getTime();
}

private static String buildParams(Map<String, String> params) {
StringBuilder str = new StringBuilder();

for (Entry<String, String> entry : params.entrySet()) {
if (str.length()>0) {
if (!str.isEmpty()) {
str.append('&');
}

Expand Down
14 changes: 9 additions & 5 deletions src/main/scala/ru/org/linux/reaction/ReactionDao.scala
Original file line number Diff line number Diff line change
Expand Up @@ -129,31 +129,35 @@ class ReactionDao(ds: DataSource, val transactionManager: PlatformTransactionMan
reaction = rs.getString("reaction"))
}

def getReactionsView(originUser: User, offset: Int, size: Int,isReactionsOn: Boolean): Seq[ReactionsView] =
def getReactionsView(originUser: User, offset: Int, size: Int,isReactionsOn: Boolean,includeDeleted: Boolean): Seq[ReactionsView] =
jdbcTemplate.queryAndMap(
if (isReactionsOn)
"WITH constants (selectedId) as ( values (?) ) " +
" select r.topic_id,r.comment_id,r.set_date, r.reaction,r.origin_user as \"target_user\", g.\"section\", g.urlname, t.title " +
" from reactions_log r " +
" join topics t ON r.topic_id = t.id AND NOT t.deleted " +
" join topics t ON r.topic_id = t.id " +
(if (!includeDeleted) { " AND NOT t.deleted" } else "" ) +
" join groups g ON t.groupid = g.id " +
" WHERE r.comment_id is null and t.userid=(select selectedId from constants) " +
" UNION ALL " +
" select r.topic_id,r.comment_id,r.set_date, r.reaction, r.origin_user, g.\"section\", g.urlname, t.title " +
" from reactions_log r " +
" join topics t ON r.topic_id = t.id AND NOT t.deleted " +
" join topics t ON r.topic_id = t.id " +
(if (!includeDeleted) { " AND NOT t.deleted" } else "") +
" JOIN comments c ON c.id = r.comment_id " +
" join groups g ON t.groupid = g.id " +
" WHERE c.userid=(select selectedId from constants) " +
(if (!includeDeleted) { " AND NOT c.deleted" } else "") +
" order by set_date desc OFFSET ? LIMIT ?"
else
"SELECT topic_id, comment_id, set_date, reaction, topics.title, " +
"COALESCE(comments.userid, topics.userid) target_user, groups.section, groups.urlname " +
"FROM reactions_log JOIN topics ON topic_id = topics.id " +
"JOIN groups ON topics.groupid = groups.id " +
"LEFT JOIN comments ON comment_id = comments.id " +
"WHERE origin_user=? AND NOT topics.deleted AND comments.deleted IS NOT TRUE " +
"ORDER BY set_date DESC OFFSET ? LIMIT ?",
"WHERE origin_user=? " +
(if (!includeDeleted) { " AND NOT topics.deleted AND comments.deleted IS NOT TRUE " } else "") +
" ORDER BY set_date DESC OFFSET ? LIMIT ?",
originUser.getId, offset, size) { case (rs, _) =>
ReactionsView(
item = ReactionsLogItem(
Expand Down
4 changes: 2 additions & 2 deletions src/main/scala/ru/org/linux/reaction/ReactionService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,8 @@ class ReactionService(userService: UserService, reactionDao: ReactionDao, topicD
r
}

def getReactionsView(originUser: User, offset: Int, size: Int,modeTo: Boolean): Seq[PreparedReactionView] = {
val items = reactionDao.getReactionsView(originUser, offset, size,modeTo)
def getReactionsView(originUser: User, offset: Int, size: Int,modeTo: Boolean,allowDeleted:Boolean): Seq[PreparedReactionView] = {
val items = reactionDao.getReactionsView(originUser, offset, size,modeTo,allowDeleted)
val textIds = items.view.map(_.item).map(i => i.commentId.getOrElse(i.topicId)).distinct.toSeq
val targetUserIds = items.view.map(_.targetUserId).distinct.toSeq

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,21 @@ class UserReactionsController(reactionService: ReactionService, userService: Use
modelAndView.addObject("url", url)
modelAndView.addObject("reactionsUrl", s"/people/${user.getNick}/reactions/to")

val showDeleted = currentUser.moderator

val items =if (mode != null) {
if ("to".equalsIgnoreCase(mode)) {
// признак включения режима "реакции на меня"
modelAndView.addObject("modeTo", 1)
// переделка url для пагинации
url = s"/people/${user.getNick}/reactions/to"
reactionService.getReactionsView(user, offset, ItemsPerPage + 1, modeTo = true)
reactionService.getReactionsView(user, offset, ItemsPerPage + 1, modeTo = true,showDeleted)
} else
throw new BadParameterException("mode", "incorrect")

} else {
// вариант по-умолчанию (мои реакции)
reactionService.getReactionsView(user, offset, ItemsPerPage + 1, modeTo = false)
reactionService.getReactionsView(user, offset, ItemsPerPage + 1, modeTo = false,showDeleted)
}

modelAndView.addObject("items", items.take(ItemsPerPage).asJava)
Expand Down
6 changes: 4 additions & 2 deletions src/main/scala/ru/org/linux/search/SearchViewer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.sksamuel.elastic4s.ElasticDsl.*
import com.sksamuel.elastic4s.requests.searches.SearchResponse
import com.sksamuel.elastic4s.requests.searches.queries.Query
import com.sksamuel.elastic4s.requests.searches.queries.funcscorer.WeightScore
import org.joda.time.DateTimeZone

import scala.concurrent.Await
import scala.concurrent.duration.*
Expand Down Expand Up @@ -54,10 +55,11 @@ class SearchViewer(query: SearchRequest, elastic: ElasticClient) {
}
}

def performSearch: SearchResponse = {
def performSearch(tz:DateTimeZone): SearchResponse = {
val typeFilter = Option(query.getRange.getValue) map { value =>
termQuery(query.getRange.getColumn, value)
}
val selectedDateFilter = rangeQuery(query.getInterval.getColumn) gte query.atStartOfDaySelected(tz) lte query.atEndOfDaySelected(tz)

val dateFilter = Option(query.getInterval.getRange) map { range =>
rangeQuery(query.getInterval.getColumn) gt range
Expand All @@ -71,7 +73,7 @@ class SearchViewer(query: SearchRequest, elastic: ElasticClient) {
}
}

val queryFilters = (typeFilter ++ dateFilter ++ userFilter).toSeq
val queryFilters = (typeFilter ++ (if (query.isDateSelected) Option(selectedDateFilter) else dateFilter) ++ userFilter).toSeq

val esQuery = wrapQuery(boost(processQueryString(query.getQ)), queryFilters)

Expand Down
32 changes: 27 additions & 5 deletions src/main/webapp/WEB-INF/jsp/search.jsp
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,22 @@
<form:select path="range" items="${ranges}"/>
</div>

<div class="control-group">
<label>За:
<form:select path="interval" items="${intervals}"/>
</label>
</div>
<c:choose>
<c:when test="${query.dateSelected}">
<div class="control-group">
<label>на дату</label>
<form:input id="selectedDt" path="dt" type="hidden" />
<input id="selectDt" class="input" type="text" readonly="true"/>
</div>
</c:when>
<c:otherwise>
<div class="control-group">
<label>За:
<form:select path="interval" items="${intervals}"/>
</label>
</div>
</c:otherwise>
</c:choose>

<div class="control-group">
<label>Пользователь: <form:input path="user" type="text" size="20"/></label>
Expand Down Expand Up @@ -157,4 +168,15 @@
</c:if>
</form:form>

<script type="text/javascript">
$script.ready(["jquery"], function() {
var selectedDate = document.getElementById('selectedDt');
if (selectedDate) {
d = moment(selectedDate.value,'x').format('DD.MM.YYYY');
console.log("selected: ",selectedDate,"d:",d);
document.getElementById('selectDt').value=d;
}
});
</script>

<jsp:include page="/WEB-INF/jsp/footer.jsp"/>
5 changes: 4 additions & 1 deletion src/main/webapp/WEB-INF/jsp/whois.jsp
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,10 @@
subDomainTitleFormat: {
empty: "{date}",
filled: "{date}<br>сообщений: {count}"
}
},
onClick: function (date, nb) {
window.open('/search.jsp?dt='+date.getTime()+'&user=${user.nick}', '_blank');
}
});
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class SearchResultServiceIntegrationSpec extends SpecificationWithJUnit {

"SearchResultsService" should {
"prepare some results" in new IndexFixture {
val response = new SearchViewer(new SearchRequest(), elastic).performSearch
val response = new SearchViewer(new SearchRequest(), elastic).performSearch(null)

val prepared = response.hits.hits.map(service.prepare)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class SearchViewerIntegrationSpec extends SpecificationWithJUnit {

"SearchViewer" should {
"make valid default search" in new IndexFixture {
val response = new SearchViewer(new SearchRequest(), elastic).performSearch
val response = new SearchViewer(new SearchRequest(), elastic).performSearch(null)

response.totalHits must be equalTo 0
}
Expand Down

0 comments on commit c002379

Please sign in to comment.