From 874de42562bd9799ca122b9e2c8edc971fc29293 Mon Sep 17 00:00:00 2001 From: Anwar khanfir <93767376+akhanfir@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:41:08 +0100 Subject: [PATCH] fix: filter dsiplays only parent notes issue - EXO-75447 - Meeds-io/meeds#2616. (#1226) Before this change, when open drawer of note filter and serach for a sub-level note, only first level parent notes are displayed and sub-level notes are never displayed. To resolve this problem, consume the rest of the unified search filter service for the published notes and for the draft filter using the list already retrieved. After this change, Sub-level notes is displayed in notes filter search results. --- .../exoplatform/wiki/jpa/JPADataStorage.java | 1 + .../WikiElasticSearchServiceConnector.java | 53 +++++++++++-- .../wiki/service/rest/NotesRestService.java | 8 +- .../wiki/service/search/SearchData.java | 2 + .../service/search/TitleSearchResult.java | 2 + ...WikiElasticSearchServiceConnectorTest.java | 2 +- .../wiki/service/TestWikiRestService.java | 2 +- .../service/rest/NotesRestServiceTest.java | 2 +- .../javascript/eXo/wiki/notesService.js | 16 +++- .../notes/components/NoteTreeviewDrawer.vue | 77 ++++++++++++++++--- 10 files changed, 141 insertions(+), 24 deletions(-) diff --git a/notes-service/src/main/java/org/exoplatform/wiki/jpa/JPADataStorage.java b/notes-service/src/main/java/org/exoplatform/wiki/jpa/JPADataStorage.java index dce8081b7a..76c3b19411 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/jpa/JPADataStorage.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/jpa/JPADataStorage.java @@ -149,6 +149,7 @@ public PageList search(WikiSearchData wikiSearchData) { wikiSearchData.getUserId(), wikiSearchData.getTagNames(), wikiSearchData.isFavorites(), + wikiSearchData.isNotesTreeFilter(), (int) wikiSearchData.getOffset(), wikiSearchData.getLimit()); diff --git a/notes-service/src/main/java/org/exoplatform/wiki/jpa/search/WikiElasticSearchServiceConnector.java b/notes-service/src/main/java/org/exoplatform/wiki/jpa/search/WikiElasticSearchServiceConnector.java index 526bf6d9e3..76dc41f69e 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/jpa/search/WikiElasticSearchServiceConnector.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/jpa/search/WikiElasticSearchServiceConnector.java @@ -78,6 +78,21 @@ public class WikiElasticSearchServiceConnector extends ElasticSearchServiceConne } """; + public static final String SEARCH_QUERY_TERM_FOR_NOTES_APPLICATION = """ + ,"must": [ + @wildcard_query@ + ] + """; + + public static final String WILDCARD_QUERY = """ + { + "wildcard": { + "name": "*@term@*" + } + } + """; + + public WikiElasticSearchServiceConnector(ConfigurationManager configurationManager, InitParams initParams, @@ -120,13 +135,13 @@ protected String getSourceFields() { return StringUtils.join(sourceFields, ","); } - public List searchWiki(String searchedText, String userId, List tagNames, boolean isFavorites, int offset, int limit) { - return filteredWikiSearch(searchedText, userId, tagNames, isFavorites, offset, limit); + public List searchWiki(String searchedText, String userId, List tagNames, boolean isFavorites, boolean isNotesTreeFilter, int offset, int limit) { + return filteredWikiSearch(searchedText, userId, tagNames, isFavorites, isNotesTreeFilter, offset, limit); } - protected List filteredWikiSearch(String query, String userId, List tagNames, boolean isFavorites, int offset, int limit) { + protected List filteredWikiSearch(String query, String userId, List tagNames, boolean isFavorites,boolean isNotesTreeFilter , int offset, int limit) { Set ids = getUserSpaceIds(userId); - String esQuery = buildQueryStatement(ids, userId, tagNames, query, isFavorites, offset, limit); + String esQuery = buildQueryStatement(ids, userId, tagNames, query, isFavorites, isNotesTreeFilter,offset, limit); String jsonResponse = getClient().sendRequest(esQuery, getIndex()); return buildWikiResult(jsonResponse); } @@ -153,11 +168,33 @@ private String buildTagsQueryStatement(List values) { .toString(); } - private String buildTermQuery(String termQuery) { + private String buildTermQuery(String termQuery, boolean isNotesTreeFilter) { if (StringUtils.isBlank(termQuery)) { return ""; } + termQuery = removeSpecialCharacters(termQuery); + if (isNotesTreeFilter) { + termQuery = termQuery.trim().replaceAll("\\s+", " "); + List listTermQuery = List.of(termQuery.split(" ")); + if (listTermQuery.size() == 0) { + return ""; + } + String allWildcardQuery = ""; + String termWildcardQuery = WILDCARD_QUERY.replace("@term@",listTermQuery.get(0)); + allWildcardQuery = allWildcardQuery.concat(termWildcardQuery); + if (listTermQuery.size() > 1) { + int index = 0; + for(String term : listTermQuery) { + if (index > 0){ + termWildcardQuery = WILDCARD_QUERY.replace("@term@", term); + allWildcardQuery = allWildcardQuery.concat(",").concat(termWildcardQuery); + } + index++; + }; + } + return SEARCH_QUERY_TERM_FOR_NOTES_APPLICATION.replace("@wildcard_query@", allWildcardQuery); + } else { List termsQuery = Arrays.stream(termQuery.split(" ")).filter(StringUtils::isNotBlank).map(word -> { word = word.trim(); if (word.length() > 5) { @@ -165,7 +202,8 @@ private String buildTermQuery(String termQuery) { } return word; }).toList(); - return SEARCH_QUERY_TERM.replace("@term@", StringUtils.join(termsQuery, " ")); + return SEARCH_QUERY_TERM.replace("@term@", StringUtils.join(termsQuery, " ")); + } } private String buildQueryStatement(Set calendarOwnersOfUser, @@ -173,13 +211,14 @@ private String buildQueryStatement(Set calendarOwnersOfUser, List tagNames, String term, boolean isFavorites, + boolean isNotesTreeFilter, long offset, long limit) { term = removeSpecialCharacters(term); Map> metadataFilters = buildMetadataFilter(isFavorites, userId); String metadataQuery = buildMetadataQueryStatement(metadataFilters); String tagsQuery = buildTagsQueryStatement(tagNames); - String termsQuery = buildTermQuery(term); + String termsQuery = buildTermQuery(term, isNotesTreeFilter); return retrieveSearchQuery().replace("@term_query@", termsQuery) .replace("@metadatas_query@", metadataQuery) .replace("@tags_query@", tagsQuery) diff --git a/notes-service/src/main/java/org/exoplatform/wiki/service/rest/NotesRestService.java b/notes-service/src/main/java/org/exoplatform/wiki/service/rest/NotesRestService.java index 027989d0d9..131be8d319 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/service/rest/NotesRestService.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/service/rest/NotesRestService.java @@ -1270,7 +1270,9 @@ public Response searchData(@Context int limit, @QueryParam("wikiType") String wikiType, @QueryParam("wikiOwner") String wikiOwner, @QueryParam("favorites") - boolean favorites, @QueryParam("tags") List tagNames) throws Exception { + boolean favorites, @QueryParam("tags") + List tagNames, @QueryParam("isNotesTreeFilter") + boolean isNotesTreeFilter) throws Exception { limit = limit > 0 ? limit : RestUtils.getLimit(uriInfo); try { @@ -1278,6 +1280,7 @@ public Response searchData(@Context Identity currentIdentity = ConversationState.getCurrent().getIdentity(); WikiSearchData data = new WikiSearchData(keyword, currentIdentity.getUserId()); data.setLimit(limit); + data.setNotesTreeFilter(isNotesTreeFilter); data.setFavorites(favorites); data.setTagNames(tagNames); List results = noteService.search(data).getAll(); @@ -1296,6 +1299,7 @@ public Response searchData(@Context TitleSearchResult titleSearchResult = new TitleSearchResult(); titleSearchResult.setTitle(attachment.getName()); titleSearchResult.setId(page.getId()); + titleSearchResult.setPageName(page.getName()); titleSearchResult.setActivityId(page.getActivityId()); titleSearchResult.setType(searchResult.getType()); titleSearchResult.setUrl(attachment.getDownloadURL()); @@ -1319,6 +1323,8 @@ public Response searchData(@Context TitleSearchResult titleSearchResult = new TitleSearchResult(); titleSearchResult.setTitle(searchResult.getTitle()); titleSearchResult.setId(page.getId()); + titleSearchResult.setPageName(page.getName()); + titleSearchResult.setPageName(page.getName()); titleSearchResult.setActivityId(page.getActivityId()); if (posterIdentity != null) { titleSearchResult.setPoster(posterIdentity); diff --git a/notes-service/src/main/java/org/exoplatform/wiki/service/search/SearchData.java b/notes-service/src/main/java/org/exoplatform/wiki/service/search/SearchData.java index dff6eafc05..943310f577 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/service/search/SearchData.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/service/search/SearchData.java @@ -47,6 +47,8 @@ public class SearchData { private List tagNames; + private boolean isNotesTreeFilter; + public SearchData(String title, String content, String wikiType, String wikiOwner, String pageId, String userId) { this.title = org.exoplatform.wiki.utils.Utils.escapeIllegalCharacterInQuery(title); this.content = org.exoplatform.wiki.utils.Utils.escapeIllegalCharacterInQuery(content); diff --git a/notes-service/src/main/java/org/exoplatform/wiki/service/search/TitleSearchResult.java b/notes-service/src/main/java/org/exoplatform/wiki/service/search/TitleSearchResult.java index b940bf4b3b..89fd3f72dd 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/service/search/TitleSearchResult.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/service/search/TitleSearchResult.java @@ -49,4 +49,6 @@ public class TitleSearchResult { private String lang; + private String pageName; + } diff --git a/notes-service/src/test/java/org/exoplatform/wiki/jpa/search/WikiElasticSearchServiceConnectorTest.java b/notes-service/src/test/java/org/exoplatform/wiki/jpa/search/WikiElasticSearchServiceConnectorTest.java index 3261027833..00a2c580f5 100644 --- a/notes-service/src/test/java/org/exoplatform/wiki/jpa/search/WikiElasticSearchServiceConnectorTest.java +++ b/notes-service/src/test/java/org/exoplatform/wiki/jpa/search/WikiElasticSearchServiceConnectorTest.java @@ -144,7 +144,7 @@ protected String getPermissionFilter() { // when List tagNames = new ArrayList<>(); tagNames.add("testNoteTag"); - List searchResults = searchServiceConnector.searchWiki("*","__system", tagNames, false, 0, 20); + List searchResults = searchServiceConnector.searchWiki("*","__system", tagNames, false, false, 0, 20); // Then assertNotNull(searchResults); diff --git a/notes-service/src/test/java/org/exoplatform/wiki/service/TestWikiRestService.java b/notes-service/src/test/java/org/exoplatform/wiki/service/TestWikiRestService.java index 9637eaf2c0..06fe396eb6 100644 --- a/notes-service/src/test/java/org/exoplatform/wiki/service/TestWikiRestService.java +++ b/notes-service/src/test/java/org/exoplatform/wiki/service/TestWikiRestService.java @@ -126,7 +126,7 @@ public void testSearchData() throws Exception { // When List tagNames = new ArrayList<>(); tagNames.add("testTag"); - Response response = wikiRestService.searchData(uriInfo, "wiki", 10, "page", "alioua", false, tagNames); + Response response = wikiRestService.searchData(uriInfo, "wiki", 10, "page", "alioua", false, tagNames, false); // Then assertEquals(200, response.getStatus()); diff --git a/notes-service/src/test/java/org/exoplatform/wiki/service/rest/NotesRestServiceTest.java b/notes-service/src/test/java/org/exoplatform/wiki/service/rest/NotesRestServiceTest.java index ff30a3f5c4..15f50c717a 100644 --- a/notes-service/src/test/java/org/exoplatform/wiki/service/rest/NotesRestServiceTest.java +++ b/notes-service/src/test/java/org/exoplatform/wiki/service/rest/NotesRestServiceTest.java @@ -445,7 +445,7 @@ public void testSearchData() throws Exception { anyString())) .thenReturn(identityEntity); - Response response = notesRestService.searchData(uriInfo, "test", 10, "wikiType", "wikiOwner", true, new ArrayList<>()); + Response response = notesRestService.searchData(uriInfo, "test", 10, "wikiType", "wikiOwner", true, new ArrayList<>(), false); assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); } diff --git a/notes-webapp/src/main/webapp/javascript/eXo/wiki/notesService.js b/notes-webapp/src/main/webapp/javascript/eXo/wiki/notesService.js index 1b039df146..8afd10067a 100644 --- a/notes-webapp/src/main/webapp/javascript/eXo/wiki/notesService.js +++ b/notes-webapp/src/main/webapp/javascript/eXo/wiki/notesService.js @@ -1,7 +1,7 @@ import { notesConstants } from './notesConstants.js'; export function getNote(noteBookType, noteBookOwner, noteId,source,lang) { - let url = `${notesConstants.PORTAL}/${notesConstants.PORTAL_REST}/notes/note/${noteBookType}/${noteBookOwner}/${noteId}`; +let url = `${notesConstants.PORTAL}/${notesConstants.PORTAL_REST}/notes/note/${noteBookType}/${noteBookOwner}/${noteId}`; if (source){ url=`${url}?source=${source}`; } @@ -429,3 +429,17 @@ export function removeNoteFeaturedImage(noteId, isDraft, lang) { } }); } + +export function searchNotes(keyword, limit) { + document.dispatchEvent(new CustomEvent('displayTopBarLoading')); + return fetch(`${notesConstants.PORTAL}/${notesConstants.PORTAL_REST}/notes/contextsearch?keyword=${keyword}&isNotesTreeFilter=true&limit=${limit}`, { + method: 'GET', + credentials: 'include', + }).then(resp => { + if (!resp || !resp.ok) { + throw new Error('Response code indicates a server error', resp); + } else { + return resp.json(); + } + }).finally(() => document.dispatchEvent(new CustomEvent('hideTopBarLoading'))); +} \ No newline at end of file diff --git a/notes-webapp/src/main/webapp/vue-app/notes/components/NoteTreeviewDrawer.vue b/notes-webapp/src/main/webapp/vue-app/notes/components/NoteTreeviewDrawer.vue index 0eb7579c36..09345e608e 100644 --- a/notes-webapp/src/main/webapp/vue-app/notes/components/NoteTreeviewDrawer.vue +++ b/notes-webapp/src/main/webapp/vue-app/notes/components/NoteTreeviewDrawer.vue @@ -470,7 +470,12 @@ export default { dataCreated: false, isLoading: false, selectionType: 'independent', - inProgressTreeFetches: [] + inProgressTreeFetches: [], + filterItems: [], + filterItemsDraft: [], + limit: 20, + timeout: 1000, + searchTimer: null, }), props: { selectedTranslation: { @@ -498,7 +503,7 @@ export default { }, active() { return this.search - && this.allItems + && this.allItems && this.allItems.filter(item => item.name.toLowerCase().match(this.search.toLowerCase())) || this.activeItem; }, @@ -533,24 +538,26 @@ export default { this.isLoading = this.inProgressTreeFetches?.length; }, search() { + clearTimeout(this.searchTimer); this.showTree = true; if (this.search) { - this.items = this.active; - this.items.forEach(item => { - item.children = null; - }); - this.showTree = !!this.active.length; + this.searchTerm(); } else { this.retrieveNoteTree(this.note.wikiType, this.note.wikiOwner, this.note.name); } }, filter() { - if (this.note && this.note.id) { - if (this.note.draftPage) { - this.getDraftNote(this.note.id); - } else { - this.getNoteById(this.note.id); + if (!this.search) { + this.items = []; + if (this.note && this.note.id) { + if (this.note.draftPage) { + this.getDraftNote(this.note.id); + } else { + this.getNoteById(this.note.id); + } } + } else { + this.searchTerm(); } }, }, @@ -822,6 +829,9 @@ export default { } if (this.isDraftFilter) { this.naturalSort(this.items); + this.filterItems = this.items; + this.filterItemsDraft = []; + this.filterItemsForSearch(this.filterItems); } this.allItems = data.treeNodeData; this.allItemsHome = data.jsonList[0].children; @@ -892,6 +902,49 @@ export default { this.exporting = false; this.$nextTick().then(() => this.close()); } + }, + searchTerm() { + this.items = []; + this.isLoading = true; + this.searchTimer = setTimeout(() => { + if (this.isDraftFilter) { + this.items = this.filterItemsDraft.filter(item => item.name.includes(this.search)); + } else { + this.$notesService.searchNotes(this.search, this.limit).then(data => { + this.items = data?.jsonList.length ? this.toListNotes(data?.jsonList) : []; + this.showTree = !!this.items.length; + }); + } + this.isLoading = false; + }, this.timeout); + }, + filterItemsForSearch(filterItems){ + filterItems.forEach(filterItem => { + if (filterItem.draftPage) { + this.filterItemsDraft.push(filterItem); + } + if (filterItem.hasChild) { + this.filterItemsForSearch(filterItem.children); + } + }); + }, + toListNotes(items) { + const itemsNotes = []; + items.forEach(item => itemsNotes.push(this.toNote(item))); + return itemsNotes; + }, + toNote(note) { + return { + children: [], + disabled: false, + draftPage: false, + name: note.title, + nodeType: note.type, + noteId: note.id, + url: note.url, + path: note.pageName, + parentPageId: '' + }; } } };