Skip to content

Commit

Permalink
MODNOTES-163 Support search by title and content (#163)
Browse files Browse the repository at this point in the history
  • Loading branch information
psmagin authored Dec 10, 2020
1 parent 0d674c8 commit 6fce921
Show file tree
Hide file tree
Showing 10 changed files with 100 additions and 40 deletions.
26 changes: 26 additions & 0 deletions .rancher-pipeline.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
stages:
- name: Build
steps:
- runScriptConfig:
image: maven:3-openjdk-11
shellScript: mvn package -DskipTests
- name: Build Docker with DIND
steps:
- publishImageConfig:
dockerfilePath: ./Dockerfile
buildContext: .
tag: docker.dev.folio.org/mod-notes:spitfire-latest
pushRemote: true
registry: docker.dev.folio.org
- name: Deploy
steps:
- applyAppConfig:
catalogTemplate: p-9tp2k:spitfire-helmcharts-mod-notes
version: 0.1.32
answers:
image.repository: docker.dev.folio.org/mod-notes
image.tag: spitfire-latest
name: mod-notes
targetNamespace: spitfire
timeout: 60
notification: {}
2 changes: 1 addition & 1 deletion descriptors/ModuleDescriptor-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"provides": [
{
"id": "notes",
"version": "1.0",
"version": "2.0",
"handlers": [
{
"methods": ["GET"],
Expand Down
7 changes: 3 additions & 4 deletions ramls/link.raml
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,10 @@ traits:
]
description: Return a list of notes by status
queryParameters:
title:
displayName: Note title
search:
displayName: Search term
type: string
description: Search string for note title. Note is returned only if it contains
specified word or sequence of words anywhere in the title. Search is case-insensitive.
description: Partial match case-insensitive search term for note title and note details.
example: important
required: false
noteType:
Expand Down
16 changes: 8 additions & 8 deletions src/main/java/org/folio/links/NoteLinksConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ private NoteLinksConstants() {

static final String ORDER_BY_TITLE_CLAUSE = "ORDER BY data.jsonb->>'title' %s ";

static final String ORDER_BY_CONTENT_CLAUSE = "ORDER BY data.jsonb->>'title' || regexp_replace(data.jsonb->>'content', E'<[^>]+>', '', 'gi') %s ";
static final String ORDER_BY_CONTENT_CLAUSE = "ORDER BY search_content %s ";

static final String ORDER_BY_LINKS_NUMBER = "ORDER BY json_array_length((data.jsonb->>'links')::json) %s ";

Expand All @@ -55,17 +55,17 @@ private NoteLinksConstants() {

private static final String JOIN_NOTE_TYPE_TABLE = "LEFT JOIN %s as type on (data.jsonb -> 'typeId' = type.jsonb -> 'id') ";

private static final String WHERE_CLAUSE_BY_DOMAIN_AND_TITLE =
"WHERE (data.jsonb->>'domain' = ?) AND (f_unaccent(data.jsonb->>'title') ~* f_unaccent(?)) ";
private static final String WHERE_CLAUSE_BY_DOMAIN_AND_CONTENT =
"WHERE (data.jsonb->>'domain' = ?) AND search_content ~* f_unaccent(?) ";

static final String WHERE_CLAUSE_BY_NOTE_TYPE = " AND (type.jsonb ->> 'name' IN (%s)) ";

static final String SELECT_NOTES_BY_DOMAIN_AND_TITLE =
"SELECT data.id, data.jsonb FROM %s as data " + JOIN_NOTE_TYPE_TABLE + WHERE_CLAUSE_BY_DOMAIN_AND_TITLE;
static final String SELECT_NOTES_BY_DOMAIN_AND_CONTENT =
"SELECT data.id, data.jsonb FROM %s as data " + JOIN_NOTE_TYPE_TABLE + WHERE_CLAUSE_BY_DOMAIN_AND_CONTENT;

static final String COUNT_NOTES_BY_DOMAIN_AND_TITLE =
"SELECT COUNT(data.id) as count FROM %s as data " + JOIN_NOTE_TYPE_TABLE + WHERE_CLAUSE_BY_DOMAIN_AND_TITLE;
static final String COUNT_NOTES_BY_DOMAIN_AND_CONTENT =
"SELECT COUNT(data.id) as count FROM %s as data " + JOIN_NOTE_TYPE_TABLE + WHERE_CLAUSE_BY_DOMAIN_AND_CONTENT;

static final String ANY_STRING_PATTERN = ".*";
static final String WORD_PATTERN = "\\m%s\\M";
static final String WORD_PATTERN = ".*%s.*";
}
4 changes: 2 additions & 2 deletions src/main/java/org/folio/links/NoteLinksRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ public interface NoteLinksRepository {

Future<Void> update(Link link, List<String> assignNotes, List<String> unAssignNotes, String tenantId);

Future<NoteCollection> findNotesByTitleAndNoteTypeAndStatus(EntityLink link, String title, List<String> noteTypes,
Future<NoteCollection> findNotesByTitleAndNoteTypeAndStatus(EntityLink link, String search, List<String> noteTypes,
Status status, OrderBy orderBy, Order order,
RowPortion rowPortion, String tenantId);

Future<Integer> countNotesByTitleAndNoteTypeAndStatus(EntityLink link, String title, List<String> noteTypes, Status status,
Future<Integer> countNotesByTitleAndNoteTypeAndStatus(EntityLink link, String search, List<String> noteTypes, Status status,
String tenantId);
}
30 changes: 15 additions & 15 deletions src/main/java/org/folio/links/NoteLinksRepositoryImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import static io.vertx.core.Future.succeededFuture;

import static org.folio.links.NoteLinksConstants.ANY_STRING_PATTERN;
import static org.folio.links.NoteLinksConstants.COUNT_NOTES_BY_DOMAIN_AND_TITLE;
import static org.folio.links.NoteLinksConstants.COUNT_NOTES_BY_DOMAIN_AND_CONTENT;
import static org.folio.links.NoteLinksConstants.DELETE_NOTES_WITHOUT_LINKS;
import static org.folio.links.NoteLinksConstants.HAS_LINK_CONDITION;
import static org.folio.links.NoteLinksConstants.INSERT_LINKS;
Expand All @@ -17,7 +17,7 @@
import static org.folio.links.NoteLinksConstants.ORDER_BY_TITLE_CLAUSE;
import static org.folio.links.NoteLinksConstants.ORDER_BY_UPDATED_DATE;
import static org.folio.links.NoteLinksConstants.REMOVE_LINKS;
import static org.folio.links.NoteLinksConstants.SELECT_NOTES_BY_DOMAIN_AND_TITLE;
import static org.folio.links.NoteLinksConstants.SELECT_NOTES_BY_DOMAIN_AND_CONTENT;
import static org.folio.links.NoteLinksConstants.WHERE_CLAUSE_BY_NOTE_TYPE;
import static org.folio.links.NoteLinksConstants.WORD_PATTERN;

Expand Down Expand Up @@ -81,14 +81,14 @@ public Future<Void> update(Link link, List<String> assignNotes, List<String> unA
}

@Override
public Future<NoteCollection> findNotesByTitleAndNoteTypeAndStatus(EntityLink link, String title, List<String> noteTypes,
public Future<NoteCollection> findNotesByTitleAndNoteTypeAndStatus(EntityLink link, String search, List<String> noteTypes,
Status status,
OrderBy orderBy, Order order, RowPortion rowPortion,
String tenantId) {
Tuple parameters = Tuple.tuple();
StringBuilder queryBuilder = new StringBuilder();

addSelectClause(parameters, queryBuilder, link.getDomain(), title, tenantId);
addSelectClause(parameters, queryBuilder, link.getDomain(), search, tenantId);

addWhereNoteTypeClause(parameters, queryBuilder, noteTypes);

Expand All @@ -107,13 +107,13 @@ public Future<NoteCollection> findNotesByTitleAndNoteTypeAndStatus(EntityLink li
}

@Override
public Future<Integer> countNotesByTitleAndNoteTypeAndStatus(EntityLink link, String title, List<String> noteTypes,
public Future<Integer> countNotesByTitleAndNoteTypeAndStatus(EntityLink link, String search, List<String> noteTypes,
Status status, String tenantId) {

Tuple parameters = Tuple.tuple();
StringBuilder queryBuilder = new StringBuilder();

addSelectCountClause(parameters, queryBuilder, link.getDomain(), title, tenantId);
addSelectCountClause(parameters, queryBuilder, link.getDomain(), search, tenantId);

addWhereNoteTypeClause(parameters, queryBuilder, noteTypes);

Expand Down Expand Up @@ -288,19 +288,19 @@ private void addLimitOffset(Tuple parameters, StringBuilder query, RowPortion ro
.addInteger(rowPortion.getOffset());
}

private void addSelectClause(Tuple parameters, StringBuilder query, String domain, String title, String tenantId) {
private void addSelectClause(Tuple parameters, StringBuilder query, String domain, String search, String tenantId) {
query
.append(String.format(SELECT_NOTES_BY_DOMAIN_AND_TITLE, getNoteTableName(tenantId), getNoteTypeTableName(tenantId)));
.append(String.format(SELECT_NOTES_BY_DOMAIN_AND_CONTENT, getNoteTableName(tenantId), getNoteTypeTableName(tenantId)));
parameters
.addString(domain)
.addString(getTitleRegexp(title));
.addString(getContentRegexp(search));
}

private void addSelectCountClause(Tuple parameters, StringBuilder query, String domain, String title, String tenantId) {
query.append(String.format(COUNT_NOTES_BY_DOMAIN_AND_TITLE, getNoteTableName(tenantId), getNoteTypeTableName(tenantId)));
private void addSelectCountClause(Tuple parameters, StringBuilder query, String domain, String search, String tenantId) {
query.append(String.format(COUNT_NOTES_BY_DOMAIN_AND_CONTENT, getNoteTableName(tenantId), getNoteTypeTableName(tenantId)));
parameters
.addString(domain)
.addString(getTitleRegexp(title));
.addString(getContentRegexp(search));
}

private void addOrderByClause(Tuple parameters, StringBuilder query, Order order, OrderBy orderBy, JsonObject jsonLink) {
Expand Down Expand Up @@ -338,11 +338,11 @@ private void addWhereClause(Tuple parameters, StringBuilder query, Status status
}
}

private String getTitleRegexp(String title) {
if (StringUtils.isEmpty(title)) {
private String getContentRegexp(String str) {
if (StringUtils.isEmpty(str)) {
return ANY_STRING_PATTERN;
} else {
String regex = escapeRegex(title)
String regex = escapeRegex(str)
.replace(ESCAPED_ANY_STRING_WILDCARD, ".*");
return String.format(WORD_PATTERN, regex);
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/org/folio/rest/impl/NoteLinksImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public void putNoteLinksTypeIdByTypeAndId(String type, String id, NoteLinksPut e

@Validate
@Override
public void getNoteLinksDomainTypeIdByDomainAndTypeAndId(String domain, String type, String id, String title,
public void getNoteLinksDomainTypeIdByDomainAndTypeAndId(String domain, String type, String id, String search,
List<String> noteTypes, String status, String orderBy,
String order, int offset, int limit,
Map<String, String> okapiHeaders,
Expand All @@ -75,7 +75,7 @@ public void getNoteLinksDomainTypeIdByDomainAndTypeAndId(String domain, String t
.validate();

Future<NoteCollection> notes = validated.compose(
v -> noteLinksService.findNotesByTitleAndNoteTypeAndStatus(new EntityLink(domain, type, id), title, noteTypes,
v -> noteLinksService.findNotesByTitleAndNoteTypeAndStatus(new EntityLink(domain, type, id), search, noteTypes,
Status.enumOf(status), OrderBy.enumOf(orderBy), Order.enumOf(order),
new RowPortion(offset, limit), tenantId(okapiHeaders)));

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
ALTER TABLE note_data ADD COLUMN IF NOT EXISTS search_content text;

CREATE INDEX search_content_idx_gin ON note_data USING gin (search_content public.gin_trgm_ops);

CREATE OR REPLACE FUNCTION update_search_content()
RETURNS TRIGGER AS $$
BEGIN
NEW.search_content = f_unaccent(coalesce(NEW.jsonb->>'title','') || ' '
|| regexp_replace(
regexp_replace(
coalesce(NEW.jsonb->>'content',''),
E'<[^>]+>', '', 'gi'
),
'\n+', ' ', 'gi'
));
RETURN NEW;
END;
$$ language 'plpgsql';

DROP TRIGGER IF EXISTS update_search_content ON note_data;

CREATE TRIGGER update_search_content
BEFORE INSERT OR UPDATE ON note_data
FOR EACH ROW EXECUTE PROCEDURE update_search_content();

UPDATE note_data SET jsonb = jsonb;
5 changes: 5 additions & 0 deletions src/main/resources/templates/db_scripts/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@
"run": "after",
"snippetPath": "create_note_type_view.sql",
"fromModuleVersion": "mod-notes-2.7.0"
},
{
"run": "after",
"snippetPath": "create_note_search_column.sql",
"fromModuleVersion": "mod-notes-2.11.0"
}
]
}
20 changes: 12 additions & 8 deletions src/test/java/org/folio/rest/impl/NoteLinksImplTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -542,29 +542,33 @@ public void shouldReturn400WhenOrderByUpdatedDateParameterIsInvalid() {
}

@Test
public void shouldReturnListOfNotesSearchedByTitle() {
Note firstNote = getNote().withTitle("Title ABC");
public void shouldReturnListOfNotesSearchedByContent() {
Note firstNote = getNote().withTitle("Title ABC").withContent("<p>test content</p><p>zztest</p>");
Note secondNote = getNote().withTitle("Title ZZZ ABC");
Note thirdNote = getNote().withTitle("Title TTT");
postNoteWithOk(Json.encode(firstNote), USER8);
postNoteWithOk(Json.encode(secondNote), USER8);
postNoteWithOk(Json.encode(thirdNote), USER8);
createLinks(firstNote.getId());
createLinks(secondNote.getId());
createLinks(thirdNote.getId());

List<Note> notes = getWithOk("/note-links/domain/" + DOMAIN + "/type/" + PACKAGE_TYPE + "/id/" + PACKAGE_ID
+ "?title=ZZZ ")
+ "?search=ZZ&orderBy=content")
.as(NoteCollection.class)
.getNotes();

assertEquals(1, notes.size());
assertEquals(secondNote.getTitle(), notes.get(0).getTitle());
assertEquals(2, notes.size());
assertEquals(firstNote.getTitle(), notes.get(0).getTitle());
assertEquals(secondNote.getTitle(), notes.get(1).getTitle());
}

@Test
public void shouldReturnListOfNotesSearchedByTitleWithWildcard() {
Note firstNote = getNote().withTitle("Title ZZZ ABC");
postNoteWithOk(Json.encode(firstNote), USER8);
List<Note> notes = getWithOk("/note-links/domain/" + DOMAIN + "/type/" + PACKAGE_TYPE + "/id/" + PACKAGE_ID
+ "?title=Z*")
+ "?search=Z*")
.as(NoteCollection.class)
.getNotes();

Expand All @@ -577,7 +581,7 @@ public void shouldInterpretSpecialRegexCharactersLiterally() {
Note firstNote = getNote().withTitle("a[abc1}{]z");
postNoteWithOk(Json.encode(firstNote), USER8);
List<Note> notes = getWithOk("/note-links/domain/" + DOMAIN + "/type/" + PACKAGE_TYPE + "/id/" + PACKAGE_ID
+ "?title=a[abc1}{]z")
+ "?search=a[abc1}{]z")
.as(NoteCollection.class)
.getNotes();

Expand Down Expand Up @@ -791,7 +795,7 @@ public void shouldReturnNoteListWhenSearchByTitleAndNoteType() {

List<Note> notes = getWithOk(
"/note-links/domain/" + DOMAIN + "/type/" + PACKAGE_TYPE + "/id/" + PACKAGE_ID2 +
"?title=" + noteTitle + "&noteType=" + NOTE_TYPE2_NAME + " &noteType= " + NOTE_TYPE_NAME
"?search=" + noteTitle + "&noteType=" + NOTE_TYPE2_NAME + " &noteType= " + NOTE_TYPE_NAME
+ "&order=ASC&orderBy=status")
.as(NoteCollection.class)
.getNotes();
Expand Down

0 comments on commit 6fce921

Please sign in to comment.