OpenSearch progress. Stetic changes in result list. Faceted search not started

This commit is contained in:
Porras-Bernardez 2024-06-11 14:38:50 +02:00
parent 9b8b2bd5ac
commit a70e454cbc
6 changed files with 41 additions and 179 deletions

View File

@ -1,4 +1,6 @@
APP_URL=//tethys.at APP_URL=//tethys.at
VUE_API=//www.tethys.at VUE_API=//www.tethys.at
SOLR_HOST=tethys.at # SOLR_HOST=tethys.at
SOLR_CORE=rdr_data # SOLR_CORE=rdr_data
OPEN_HOST=192.168.21.18
OPEN_CORE=tethys-records

View File

@ -37,15 +37,13 @@ export default class VsInput extends Vue {
private loading = false; // Loading state indicator private loading = false; // Loading state indicator
private selectedIndex = -1; // Index of the currently selected suggestion private selectedIndex = -1; // Index of the currently selected suggestion
// private selectedDisplay = "";
private solr: SolrSettings = { private solr: SolrSettings = {
core: SOLR_CORE, //"rdr_data", // SOLR.core; core: SOLR_CORE, //"rdr_data", // SOLR.core;
host: SOLR_HOST, //"tethys.at", host: SOLR_HOST, //"tethys.at",
// core: "test_data", // SOLR.core;
// host: "repository.geologie.ac.at",
}; };
private open: OpenSettings = { private openSearch: OpenSettings = {
core: OPEN_CORE, //"rdr_data", // SOLR.core; core: OPEN_CORE, //"rdr_data", // SOLR.core;
host: OPEN_HOST, //"tethys.at", host: OPEN_HOST, //"tethys.at",
// core: "test_data", // SOLR.core; // core: "test_data", // SOLR.core;
@ -126,7 +124,7 @@ export default class VsInput extends Vue {
if (highlight.author && highlight.author.length > 0) { if (highlight.author && highlight.author.length > 0) {
const highlightedAuthor = highlight.author.join(" "); const highlightedAuthor = highlight.author.join(" ");
const datasetAuthor = this.find(dataset.author, this.display.toLowerCase()); const datasetAuthor = this.find(dataset.author, this.display.toLowerCase());
const hasAuthorSuggestion = suggestions.some((suggestion) => suggestion.highlight === highlightedAuthor && suggestion.type == SearchType.Author); const hasAuthorSuggestion = suggestions.some((suggestion) => suggestion.highlight.toLowerCase() === highlightedAuthor.toLowerCase() && suggestion.type == SearchType.Author);
if (!hasAuthorSuggestion) { if (!hasAuthorSuggestion) {
const suggestion = new Suggestion(datasetAuthor, highlightedAuthor, SearchType.Author); const suggestion = new Suggestion(datasetAuthor, highlightedAuthor, SearchType.Author);
suggestions.push(suggestion); suggestions.push(suggestion);
@ -135,48 +133,13 @@ export default class VsInput extends Vue {
if (highlight.subjects && highlight.subjects.length > 0) { if (highlight.subjects && highlight.subjects.length > 0) {
const highlightedSubject = highlight.subjects.join(" "); const highlightedSubject = highlight.subjects.join(" ");
const datasetSubject = this.find(dataset.subjects, this.display.toLowerCase()); const datasetSubject = this.find(dataset.subjects, this.display.toLowerCase());
const hasSubjectSuggestion = suggestions.some((suggestion) => suggestion.highlight === highlightedSubject && suggestion.type == SearchType.Subject); const hasSubjectSuggestion = suggestions.some((suggestion) => suggestion.highlight.toLowerCase() === highlightedSubject.toLowerCase() && suggestion.type == SearchType.Subject);
if (!hasSubjectSuggestion) { if (!hasSubjectSuggestion) {
const suggestion = new Suggestion(datasetSubject, highlightedSubject, SearchType.Subject); const suggestion = new Suggestion(datasetSubject, highlightedSubject, SearchType.Subject);
suggestions.push(suggestion); suggestions.push(suggestion);
} }
} }
// // Checks if a suggestion with the same title and type already exists in the suggestions array. If not, it creates a new Suggestion object and adds it to the suggestions array.
// if (highlight.title && highlight.title.length > 0) {
// /** This line checks if the highlight object has a title property and if that property is an array with at least one element.
// * The highlight object contains highlighted fragments of the search term in various fields (e.g., title, author, subjects) as returned by the OpenSearch API.
// * This check ensures that we only process results that have highlighted titles. */
// const title = highlight.title.join(" ");
// /**
// * The highlight.title property is an array of strings, where each string is a highlighted fragment of the title. join(" ") combines these fragments into a single string with spaces between them.
// * This step constructs a full highlighted title from the individual fragments.
// * OpenSearch can return multiple fragments of a field (like the title) in its response, especially when the field contains multiple terms that match the search query.
// * This can happen because OpenSearch's highlighting feature is designed to provide context around each match within the field, which can result in multiple highlighted fragments.
// */
// const hasTitleSuggestion = suggestions.some((suggestion) => suggestion.value === title && suggestion.type == SearchType.Title);
// if (!hasTitleSuggestion) {
// const suggestion = new Suggestion(title, SearchType.Title);
// suggestions.push(suggestion);
// }
// }
// if (highlight.author && highlight.author.length > 0) {
// const author = highlight.author.join(" ");
// const hasAuthorSuggestion = suggestions.some((suggestion) => suggestion.value === author && suggestion.type == SearchType.Author);
// if (!hasAuthorSuggestion) {
// const suggestion = new Suggestion(author, SearchType.Author);
// suggestions.push(suggestion);
// }
// }
// if (highlight.subjects && highlight.subjects.length > 0) {
// const subject = highlight.subjects.join(" ");
// const hasSubjectSuggestion = suggestions.some((suggestion) => suggestion.value === subject && suggestion.type == SearchType.Subject);
// if (!hasSubjectSuggestion) {
// const suggestion = new Suggestion(subject, SearchType.Subject);
// suggestions.push(suggestion);
// }
// }
// ORIGINAL SOLR =================================================================================================== // ORIGINAL SOLR ===================================================================================================
// if (dataset.title_output.toLowerCase().includes(this.display.toLowerCase())) { // if (dataset.title_output.toLowerCase().includes(this.display.toLowerCase())) {
// const title = dataset.title_output; // const title = dataset.title_output;
@ -238,6 +201,7 @@ export default class VsInput extends Vue {
and emits a search-change event with the current value of display as the argument. */ and emits a search-change event with the current value of display as the argument. */
@Emit("search-change") @Emit("search-change")
search(): string { search(): string {
console.log("search");
this.results = []; this.results = [];
// this.$emit("search", this.display) // this.$emit("search", this.display)
this.value = this.display; //(obj["title_output"]) ? obj["title_output"] : obj.id this.value = this.display; //(obj["title_output"]) ? obj["title_output"] : obj.id
@ -260,6 +224,7 @@ export default class VsInput extends Vue {
// Perform the search request // Perform the search request
private resourceSearch() { private resourceSearch() {
console.log("resourceSearch");
if (!this.display) { if (!this.display) {
this.results = []; this.results = [];
return; return;
@ -273,7 +238,7 @@ export default class VsInput extends Vue {
private request(): void { private request(): void {
console.log("request()"); console.log("request()");
// DatasetService.searchTerm(this.display, this.solr.core, this.solr.host).subscribe({ // DatasetService.searchTerm(this.display, this.solr.core, this.solr.host).subscribe({
DatasetService.searchTerm(this.display).subscribe({ DatasetService.searchTerm(this.display, this.openSearch.core, this.openSearch.host).subscribe({
// next: (res: Dataset[]) => this.dataHandler(res), // next: (res: Dataset[]) => this.dataHandler(res),
next: (res: { datasets: Dataset[], highlights: HitHighlight[] }) => this.dataHandler(res.datasets, res.highlights), next: (res: { datasets: Dataset[], highlights: HitHighlight[] }) => this.dataHandler(res.datasets, res.highlights),
error: (error: string) => this.errorHandler(error), error: (error: string) => this.errorHandler(error),

View File

@ -149,7 +149,7 @@ input {
.autocomplete-result-item { .autocomplete-result-item {
list-style: none; list-style: none;
text-align: left; text-align: left;
/* padding: 7px 10px; */ padding: 0px 0px 0px 5px; // top,right,bottom,left
cursor: pointer; cursor: pointer;
} }

View File

@ -16,130 +16,6 @@ class DatasetService {
* *
* @param {string} searchTerm - The search term to query. * @param {string} searchTerm - The search term to query.
*/ */
// async fetchDataFromOpenSearch(searchTerm: string): Promise<void> {
// // Define the OpenSearch endpoint URL
// const url = "http://opensearch.geoinformation.dev/tethys-records/_search";
// // Set the headers for the POST request
// const headers = {
// "Content-Type": "application/json",
// };
// // Construct the body of the POST request
// const body = {
// query: {
// bool: {
// // The `should` clause specifies that at least one of these conditions must match
// should: [
// {
// // Match the search term in the title field with fuzziness enabled and a boost of 3
// match: {
// title: {
// query: searchTerm,
// fuzziness: "AUTO", // Enable fuzzy search
// boost: 3 // Boosting the relevance of title matches
// }
// }
// },
// {
// // Match the search term in the author field with fuzziness enabled and a boost of 2
// match: {
// author: {
// query: searchTerm,
// fuzziness: "AUTO", // Enable fuzzy search
// boost: 2 // Boosting the relevance of author matches
// }
// }
// },
// {
// // Match the search term in the subject field with fuzziness enabled and a boost of 1
// match: {
// subject: {
// query: searchTerm,
// fuzziness: "AUTO", // Enable fuzzy search
// boost: 1 // Boosting the relevance of subject matches
// }
// }
// },
// {
// // Match the search term in the title field with a wildcard
// wildcard: {
// title: {
// value: `${searchTerm}*`, // Wildcard search for terms starting with searchTerm
// boost: 3 // Boosting the relevance of title matches
// }
// }
// },
// {
// // Match the search term in the author field with a wildcard
// wildcard: {
// author: {
// value: `${searchTerm}*`, // Wildcard search for terms starting with searchTerm
// boost: 2 // Boosting the relevance of author matches
// }
// }
// },
// {
// // Match the search term in the subject field with a wildcard
// wildcard: {
// subject: {
// value: `${searchTerm}*`, // Wildcard search for terms starting with searchTerm
// boost: 1 // Boosting the relevance of subject matches
// }
// }
// }
// ],
// // Ensure that at least one of the `should` clauses must match
// minimum_should_match: 1
// }
// },
// // Limit the number of search results to 10
// size: 10,
// // Start from the first result (pagination)
// from: 0,
// // Sort the results by the `server_date_published` field in descending order
// sort: [
// { server_date_published: { order: "desc" } }
// ],
// // Aggregations to provide facets for the `language` and `subject` fields
// aggs: {
// language: {
// terms: {
// field: "language.keyword" // Aggregate by the exact values of the `language` field
// }
// },
// subject: {
// terms: {
// field: "subjects.keyword", // Aggregate by the exact values of the `subjects` field
// size: 10 // Limit the number of aggregation buckets to 10
// }
// }
// }
// };
// try {
// // Send the POST request to the OpenSearch endpoint
// const response = await fetch(url, {
// method: "POST",
// headers: headers,
// body: JSON.stringify(body),
// });
// // Check if the response is not successful
// if (!response.ok) {
// throw new Error(`Failed to fetch data from ${url}, status: ${response.status}`);
// }
// // Parse the response JSON
// const data = await response.json();
// // Log the data from OpenSearch
// console.log("Data from OpenSearch:", data);
// console.log("Hits:", data.hits.total.value);
// } catch (error) {
// // Log any errors that occur during the fetch process
// console.error("Error fetching data:", error);
// }
// }
/* https://tethys.at/solr/rdr_data/select?&0=fl%3Did%2Clicence%2Cserver_date_published%2Cabstract_output%2Cidentifier%2Ctitle_output%2Ctitle_additional%2Cauthor%2Csubject%2Cdoctype&q=%2A /* https://tethys.at/solr/rdr_data/select?&0=fl%3Did%2Clicence%2Cserver_date_published%2Cabstract_output%2Cidentifier%2Ctitle_output%2Ctitle_additional%2Cauthor%2Csubject%2Cdoctype&q=%2A
&q.op=or&defType=edismax&qf=title%5E3%20author%5E2%20subject%5E1&indent=on&wt=json&rows=10&start=0&sort=server_date_published%20desc&facet=on&json.facet.language=%7B%20type%3A%20%22 &q.op=or&defType=edismax&qf=title%5E3%20author%5E2%20subject%5E1&indent=on&wt=json&rows=10&start=0&sort=server_date_published%20desc&facet=on&json.facet.language=%7B%20type%3A%20%22
@ -148,10 +24,19 @@ class DatasetService {
*/ */
// private openSearchUrl = "http://opensearch.geoinformation.dev/tethys-records/_search"; // private openSearchUrl = "http://opensearch.geoinformation.dev/tethys-records/_search";
private openSearchUrl = "http://192.168.21.18/tethys-records/_search"; // private openSearchUrl = "http://192.168.21.18/tethys-records/_search";
// public searchTerm(term: string): Observable<Dataset[]> { // public searchTerm(term: string): Observable<Dataset[]> {
public searchTerm(term: string): Observable<{ datasets: Dataset[], highlights: HitHighlight[] }> { public searchTerm(term: string, openCore: string, openHost: string): Observable<{ datasets: Dataset[], highlights: HitHighlight[] }> {
// OpenSearch endpoint
const host = "https://" + openHost; // When using geoinformation.dev
// const host = "http://" + openHost; // When using local OpenSearch dev endpoint
const path = "/" + openCore + "/_search";
const base = host + path;
/**
* The match query used for title, author, and subjects fields is case-insensitive by default. The standard analyzer is typically used, which lowercases the terms.
* The wildcard query is case-sensitive by default. To make it case-insensitive, it is needed to use a lowercase filter */
const lowercaseTerm = term.toLowerCase(); // Lowercase the search term
const body = { const body = {
query: { query: {
bool: { bool: {
@ -159,9 +44,9 @@ class DatasetService {
{ match: { title: { query: term, fuzziness: "AUTO", boost: 3 } } }, { match: { title: { query: term, fuzziness: "AUTO", boost: 3 } } },
{ match: { author: { query: term, fuzziness: "AUTO", boost: 2 } } }, { match: { author: { query: term, fuzziness: "AUTO", boost: 2 } } },
{ match: { subjects: { query: term, fuzziness: "AUTO", boost: 1 } } }, // In SOLR is "subject"! { match: { subjects: { query: term, fuzziness: "AUTO", boost: 1 } } }, // In SOLR is "subject"!
{ wildcard: { title: { value: `${term}*`, boost: 3 } } }, { wildcard: { title: { value: `${lowercaseTerm}*`, boost: 3 } } },
{ wildcard: { author: { value: `${term}*`, boost: 2 } } }, { wildcard: { author: { value: `${lowercaseTerm}*`, boost: 2 } } },
{ wildcard: { subjects: { value: `${term}*`, boost: 1 } } } // In SOLR is "subject"! { wildcard: { subjects: { value: `${lowercaseTerm}*`, boost: 1 } } } // In SOLR is "subject"!
], ],
minimum_should_match: 1 minimum_should_match: 1
} }
@ -190,7 +75,7 @@ class DatasetService {
* One of the key properties of this response object is _source, which is an array of documents (datasets) that match the search criteria. * One of the key properties of this response object is _source, which is an array of documents (datasets) that match the search criteria.
* It is used the pipe method to chain RxJS operators to the Observable returned by api.get. The map operator is used to transform the emitted items of the Observable. * It is used the pipe method to chain RxJS operators to the Observable returned by api.get. The map operator is used to transform the emitted items of the Observable.
*/ */
return api.post<OpenSearchResponse>(this.openSearchUrl, body).pipe( return api.post<OpenSearchResponse>(base, body).pipe(
tap(response => console.log("OpenSearchResponse:", response)), // Log the complete response tap(response => console.log("OpenSearchResponse:", response)), // Log the complete response
// tap(response => console.log("Aggre:", response.aggregations?.subjects.buckets[0])), // log the first subject of the array of subjects returned // tap(response => console.log("Aggre:", response.aggregations?.subjects.buckets[0])), // log the first subject of the array of subjects returned
// tap(response => console.log("Hits:", response.hits)), // log the first subject of the array of subjects returned // tap(response => console.log("Hits:", response.hits)), // log the first subject of the array of subjects returned
@ -206,9 +91,7 @@ class DatasetService {
// For the autocomplete search. Method to perform a search based on a term // For the autocomplete search. Method to perform a search based on a term
public searchTerm_SOLR(term: string, solrCore: string, solrHost: string): Observable<Dataset[]> { public searchTerm_SOLR(term: string, solrCore: string, solrHost: string): Observable<Dataset[]> {
// Calling the test method for // SOLR endpoint
// this.fetchDataFromOpenSearch(term);
// solr endpoint
const host = "https://" + solrHost; const host = "https://" + solrHost;
const path = "/solr/" + solrCore + "/select?"; const path = "/solr/" + solrCore + "/select?";
const base = host + path; const base = host + path;
@ -268,6 +151,12 @@ class DatasetService {
solrHost: string, solrHost: string,
start?: string, // Starting page start?: string, // Starting page
): Observable<SolrResponse> { ): Observable<SolrResponse> {
// console.log("face:", suggestion);
// console.log(activeFilterCategories);
// console.log(solrCore);
// console.log(solrHost);
// console.log(start);
// Construct Solr query parameters // Construct Solr query parameters
const host = "https://" + solrHost; const host = "https://" + solrHost;
const path = "/solr/" + solrCore + "/select?"; const path = "/solr/" + solrCore + "/select?";

View File

@ -59,6 +59,8 @@ export default class DatasetDetailComponent extends Vue {
} }
onSearch(suggestion: Suggestion | string): void { onSearch(suggestion: Suggestion | string): void {
console.log("onSearch");
const host = window.location.host; const host = window.location.host;
const parts = host.split("."); const parts = host.split(".");
if (parts[0] === "doi") { if (parts[0] === "doi") {

View File

@ -122,11 +122,15 @@ export default class SearchViewComponent extends Vue {
// Method to trigger a search // Method to trigger a search
onSearch(suggestion: Suggestion | string): void { onSearch(suggestion: Suggestion | string): void {
console.log("ONSEARCH");
// Reset active filter categories and facet results // Reset active filter categories and facet results
this.activeFilterCategories = new ActiveFilterCategories(); this.activeFilterCategories = new ActiveFilterCategories();
this.facets = new FacetResults(); this.facets = new FacetResults();
this.searchTerm = suggestion; this.searchTerm = suggestion;
console.log("This.searchterm: ", this.searchTerm);
/* Perform faceted search. The method returns an Observable, and the code subscribes to this Observable to handle the response. If the response is successful, it calls the dataHandler method /* Perform faceted search. The method returns an Observable, and the code subscribes to this Observable to handle the response. If the response is successful, it calls the dataHandler method
with the Solr response as a parameter. If there is an error, it calls the errorHandler method with the error message as a parameter */ with the Solr response as a parameter. If there is an error, it calls the errorHandler method with the error message as a parameter */
DatasetService.facetedSearch(suggestion, this.activeFilterCategories, this.solr.core, this.solr.host, undefined).subscribe({ DatasetService.facetedSearch(suggestion, this.activeFilterCategories, this.solr.core, this.solr.host, undefined).subscribe({