From 6f1b9f4c5f6ed9600c40865df6ea6e3b365ad129 Mon Sep 17 00:00:00 2001 From: frankporras Date: Thu, 12 Sep 2024 15:54:59 +0200 Subject: [PATCH] - Code cleaning for OpenSearch - Added comments - Facets menu small change --- .../face-category/facet-category.vue | 6 +- .../dataset.service - Tests with OpenSearch | 161 ++++++++ src/services/dataset.service.ts | 350 +++--------------- .../search-view/search-view-component.ts | 134 +++---- .../search-view/search-view-component.vue | 2 +- 5 files changed, 264 insertions(+), 389 deletions(-) create mode 100644 src/services/dataset.service - Tests with OpenSearch diff --git a/src/components/face-category/facet-category.vue b/src/components/face-category/facet-category.vue index b7c5c1d..bf3620a 100644 --- a/src/components/face-category/facet-category.vue +++ b/src/components/face-category/facet-category.vue @@ -76,7 +76,10 @@ export default FacetCategory; flex-grow: 1; flex-shrink: 0; /* padding: 0.75rem; */ - padding: 0.75em 2em; + padding-top: 0em; + padding-right: 2em; + padding-bottom: 0.75em; + padding-left: 2em; justify-content: left; } @@ -89,6 +92,7 @@ export default FacetCategory; } .panel-body { padding: 0 2em; + padding-bottom: 0.75em; /* Increase padding at the bottom */ } .disabled { diff --git a/src/services/dataset.service - Tests with OpenSearch b/src/services/dataset.service - Tests with OpenSearch new file mode 100644 index 0000000..db453f0 --- /dev/null +++ b/src/services/dataset.service - Tests with OpenSearch @@ -0,0 +1,161 @@ + // public facetedSearchOPEN( + // suggestion: Suggestion | string, + // activeFilterCategories: ActiveFilterCategories, + // openCore: string, + // openHost: string, + // start?: string, // Starting page + // ): Observable { + // // OpenSearch endpoint + // const host = openHost; + // const path = "/" + openCore + "/_search"; + // const base = host + path; + + // // Constructing Filters Based on Active Filter Categories + // const filters = Object.entries(activeFilterCategories).map(([category, values]) => { + // if (category === "language" || category === "year") { + // return { terms: { [category]: values } }; + // } else { + // return { terms: { [`${category}.keyword`]: values } }; + // } + // }); + // console.log("filters:", filters); + + // // Determine search term and query fields based on the suggestion type + // let query; + // if (typeof suggestion === "string") { // If suggestion is a string, append a wildcard (*) for partial matches + // const lowercaseTerm = suggestion.toLowerCase() + // query = { + // bool: { + // should: [ + // { match: { title: { query: suggestion, fuzziness: "AUTO", boost: 3 } } }, + // { match: { author: { query: suggestion, fuzziness: "AUTO", boost: 2 } } }, + // { match: { subjects: { query: suggestion, fuzziness: "AUTO", boost: 1 } } }, + // { wildcard: { title: { value: `${lowercaseTerm}*`, boost: 3 } } }, + // { wildcard: { author: { value: `${lowercaseTerm}*`, boost: 2 } } }, + // { wildcard: { subjects: { value: `${lowercaseTerm}*`, boost: 1 } } } + // ], + // minimum_should_match: 1 + // } + // }; + // } else if (suggestion instanceof Suggestion) { // If suggestion is a Suggestion object, form a specific query + // query = { + // match: { + // [suggestion.type]: { + // query: suggestion.value, + // operator: 'and' // all the terms in the query must be present in the field e.g. if is a title, the complete title + // } + // } + // }; + // } + + // // Set default value for start if not provided + // const startValue = start ? parseInt(start) : 0; + + // // console.log(activeFilterCategories); + // // console.log("mainQuery:", mainQuery); + + // // Construct the body of the OpenSearch query + // const body = { + + // query: { + // bool: { + // must: [ + // mainQuery, // Ensure the main query must be satisfied + // ...filters // Ensure all filters must be satisfied + // ] + // } + // }, + + // // // WORKS // Expected: 12 results + // // query: { + // // bool: { + // // "must": [ + // // { "term": { "language": "en" } }, + // // { "term": { "subjects.keyword": "Lower Austria" } }, + // // { "term": { "subjects.keyword": "counting data" } } + // // ], + // // } + // // }, + + // // // THIS WORKS: // Expected: 19 results + // // query: { + // // bool: { + // // must: [ + // // { "match": { "title": "Blatt" } }, + // // { "term": { "subjects": "bayern" } } + // // ], + // // } + // // }, + + // // // WORKS // Expected: 4 results + // // query: { + // // bool: { + // // "must": [ + // // { "match": { "title": "blatt" } }, + // // { "term": { "subjects": "bayern" } }, + // // { "term": { "subjects": "salzburg" } } + // // ], + // // } + // // }, + + // // // WORKS // Expected: 2 results + // // query: { + // // bool: { + // // "must": [ + // // { "match": { "title": "blatt" } }, + // // { "term": { "subjects": "ungarn" } }, + // // { "term": { "subjects": "steiermark" } } + // // ], + // // } + // // }, + + // // WORKS // Expected: 12 results + // query: { + // bool: { + // "must": [ + // { "term": { "language": "en" } }, + // { "term": { "subjects.keyword": "Lower Austria" } }, + // { "term": { "subjects.keyword": "counting data" } } + // ], + // "should": [ + // { match: { title: { query: "halger", fuzziness: "AUTO", boost: 3 } } }, + // { match: { author: { query: "halger", fuzziness: "AUTO", boost: 2 } } }, + // { match: { subjects: { query: "halger", fuzziness: "AUTO", boost: 1 } } }, + // { wildcard: { title: { value: "halger", boost: 3 } } }, + // { wildcard: { author: { value: "halger", boost: 2 } } }, + // { wildcard: { subjects: { value: "halger", boost: 1 } } } + // ], + // minimum_should_match: 1 + // } + // }, + + // size: 10, + // from: startValue, + // sort: [{ _score: { order: "desc" } }], + // track_scores: true, + + // aggs: { + // subjects: { terms: { field: "subjects.keyword", size: 1000 } }, + // language: { terms: { field: "language" } }, + // author: { terms: { field: "author.keyword", size: 1000 } }, + // year: { terms: { field: "year", size: 100 } } + // }, + + // highlight: { + // fields: { + // title: {}, + // author: {}, + // subjects: {} + // } + // } + // }; + + // console.log("mainQuery:", mainQuery); + // console.log("filters:", filters); + // console.log("body:", body); + + // // Make API call to OpenSearch and return the result + // const stations = api.post(base, body); + + // return stations; + // } diff --git a/src/services/dataset.service.ts b/src/services/dataset.service.ts index a494fba..c897b29 100644 --- a/src/services/dataset.service.ts +++ b/src/services/dataset.service.ts @@ -1,5 +1,4 @@ import api from "../api/api"; -// import { Observable, of } from "rxjs"; import { Observable } from "rxjs"; import { tap, map } from "rxjs/operators"; import { Dataset, DbDataset, Suggestion } from "@/models/dataset"; @@ -10,33 +9,23 @@ import { deserialize } from "class-transformer"; class DatasetService { /** - * Fetch data from the OpenSearch endpoint with fuzzy search enabled. - * This function allows for misspellings in the search term and boosts - * the relevance of matches in the title, author, and subject fields. - * - * @param {string} searchTerm - The search term to query. + * Search datasets with OpenSearch API, allowing for fuzzy search and boosting relevance in title, author, and subject fields. + * @param {string} searchTerm - Search query term + * @param {string} openCore - The OpenSearch core to search in + * @param {string} openHost - The OpenSearch host URL + * @returns {Observable} - Observable emitting datasets and their highlights */ - - /* 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 - terms%22%2C%20field%3A%20%22language%22%20%7D&json.facet.subject=%7B%20type%3A%20%22terms%22%2C%20field%3A%20%22subject%22%2C%20limit%3A%20-1%20%7D&json.facet.year=%7B%20type%3A%20%22 - terms%22%2C%20field%3A%20%22year%22%20%7D&json.facet.author=%7B%20type%3A%20%22terms%22%2C%20field%3A%20%22author_facet%22%2C%20limit%3A%20-1%20%7D - */ - - // private openSearchUrl = "http://opensearch.geoinformation.dev/tethys-records/_search"; - // private openSearchUrl = "http://192.168.21.18/tethys-records/_search"; - public searchTerm(term: string, openCore: string, openHost: string): Observable<{ datasets: Dataset[], highlights: HitHighlight[] }> { - // console.log("SEARCHTERM"); - - // OpenSearch endpoint - const host = openHost; // When using local OpenSearch dev endpoint - const path = "/" + openCore + "/_search"; - const base = host + path; + + const host = openHost; // OpenSearch host URL + const path = "/" + openCore + "/_search"; // API endpoint for searching + const base = host + path; // Complete URL for the request /** * 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 + + // Request body defining search query logic const body = { query: { bool: { @@ -50,13 +39,13 @@ class DatasetService { { wildcard: { subjects: { value: `${lowercaseTerm}*`, boost: 1 } } }, // In SOLR is "subject"! { wildcard: { doctype: { value: `${lowercaseTerm}*`, boost: 1 } } } // doctype ], - minimum_should_match: 1 + minimum_should_match: 1 // Require at least one match } }, - size: 10, - from: 0, + size: 10, // Limit to 10 results + from: 0, // Pagination: start from the first result + sort: [{ _score: { order: "desc" } }], // Sort by relevance (_score) // sort: [{ server_date_published: { order: "desc" } }], - sort: [{ _score: { order: "desc" } }], // Sort by _score in descending order track_scores: true, // This ensures "_score" is included even when sorting by other criteria. Otherwise the relevance score is not calculated aggs: { subjects: { terms: { field: "subjects.keyword", size: 1000 } }, // In SOLR is "subject"! @@ -65,37 +54,23 @@ class DatasetService { year: { terms: { field: "year", size: 100 } }, // << ".keyword" HAS TO BE REMOVED. OTHERWISE BUCKETS ARE NOT OBTAINED FOR THIS doctype: { terms: { field: "doctype", size: 50 } } // << ".keyword" HAS TO BE REMOVED. OTHERWISE BUCKETS ARE NOT OBTAINED FOR THIS }, - // // CONTABO ================================================================================ - // aggs: { - // subjects: { terms: { field: "subjects.keyword", size: 1000 } }, // In SOLR is "subject"! - // language: { terms: { field: "language.keyword" } }, // << ".keyword" HAS TO BE REMOVED. OTHERWISE BUCKETS ARE NOT OBTAINED FOR THIS - // author: { terms: { field: "author.keyword", size: 1000 } }, - // year: { terms: { field: "year.keyword", size: 100 } } // << ".keyword" HAS TO BE REMOVED. OTHERWISE BUCKETS ARE NOT OBTAINED FOR THIS - // }, - // // =========================================================================================== highlight: { fields: { - title: {}, - author: {}, - subjects: {}, - doctype: {} + title: {}, // Highlight matching terms in title + author: {}, // Highlight matching terms in author + subjects: {}, // Highlight matching terms in subjects + doctype: {} // Highlight matching terms in document type } } }; - // Make API call to OpenSearch and return the result /** + * Make API call to OpenSearch and return the result * When a POST request is made to the OpenSearch server using the api.post method, the response received from OpenSearch is an object that includes various details about the search results. * 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. */ return api.post(base, body).pipe( - // 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("Hits:", response.hits)), // log the first subject of the array of subjects returned - - // map(response => response.hits.hits.map(hit => hit._source)) - map(response => ({ datasets: response.hits.hits.map(hit => hit._source), highlights: response.hits.hits.map(hit => hit.highlight) @@ -104,7 +79,7 @@ class DatasetService { } // // For the autocomplete search. Method to perform a search based on a term - // public searchTerm_SOLR(term: string, solrCore: string, solrHost: string): Observable { + // public searchTermSOLR(term: string, solrCore: string, solrHost: string): Observable { // // SOLR endpoint // const host = "https://" + solrHost; // const path = "/solr/" + solrCore + "/select?"; @@ -123,7 +98,6 @@ class DatasetService { // "doctype", // ].toString(); - // const qfFields = "title^3 author^2 subject^1"; // const q_params = { @@ -146,193 +120,33 @@ class DatasetService { // return stations; // } - // public facetedSearchOPEN( - // suggestion: Suggestion | string, - // activeFilterCategories: ActiveFilterCategories, - // openCore: string, - // openHost: string, - // start?: string, // Starting page - // ): Observable { - // // OpenSearch endpoint - // const host = openHost; - // const path = "/" + openCore + "/_search"; - // const base = host + path; - - // // Constructing Filters Based on Active Filter Categories - // const filters = Object.entries(activeFilterCategories).map(([category, values]) => { - // if (category === "language" || category === "year") { - // return { terms: { [category]: values } }; - // } else { - // return { terms: { [`${category}.keyword`]: values } }; - // } - // }); - // console.log("filters:", filters); - // // Determine search term and query fields based on the suggestion type - // let query; - // if (typeof suggestion === "string") { // If suggestion is a string, append a wildcard (*) for partial matches - // const lowercaseTerm = suggestion.toLowerCase() - // query = { - // bool: { - // should: [ - // { match: { title: { query: suggestion, fuzziness: "AUTO", boost: 3 } } }, - // { match: { author: { query: suggestion, fuzziness: "AUTO", boost: 2 } } }, - // { match: { subjects: { query: suggestion, fuzziness: "AUTO", boost: 1 } } }, - // { wildcard: { title: { value: `${lowercaseTerm}*`, boost: 3 } } }, - // { wildcard: { author: { value: `${lowercaseTerm}*`, boost: 2 } } }, - // { wildcard: { subjects: { value: `${lowercaseTerm}*`, boost: 1 } } } - // ], - // minimum_should_match: 1 - // } - // }; - // } else if (suggestion instanceof Suggestion) { // If suggestion is a Suggestion object, form a specific query - // query = { - // match: { - // [suggestion.type]: { - // query: suggestion.value, - // operator: 'and' // all the terms in the query must be present in the field e.g. if is a title, the complete title - // } - // } - // }; - // } - - // // Set default value for start if not provided - // const startValue = start ? parseInt(start) : 0; - - // // console.log(activeFilterCategories); - // // console.log("mainQuery:", mainQuery); - - // // Construct the body of the OpenSearch query - // const body = { - - // query: { - // bool: { - // must: [ - // mainQuery, // Ensure the main query must be satisfied - // ...filters // Ensure all filters must be satisfied - // ] - // } - // }, - - // // // WORKS // Expected: 12 results - // // query: { - // // bool: { - // // "must": [ - // // { "term": { "language": "en" } }, - // // { "term": { "subjects.keyword": "Lower Austria" } }, - // // { "term": { "subjects.keyword": "counting data" } } - // // ], - // // } - // // }, - - // // // THIS WORKS: // Expected: 19 results - // // query: { - // // bool: { - // // must: [ - // // { "match": { "title": "Blatt" } }, - // // { "term": { "subjects": "bayern" } } - // // ], - // // } - // // }, - - // // // WORKS // Expected: 4 results - // // query: { - // // bool: { - // // "must": [ - // // { "match": { "title": "blatt" } }, - // // { "term": { "subjects": "bayern" } }, - // // { "term": { "subjects": "salzburg" } } - // // ], - // // } - // // }, - - // // // WORKS // Expected: 2 results - // // query: { - // // bool: { - // // "must": [ - // // { "match": { "title": "blatt" } }, - // // { "term": { "subjects": "ungarn" } }, - // // { "term": { "subjects": "steiermark" } } - // // ], - // // } - // // }, - - // // WORKS // Expected: 12 results - // query: { - // bool: { - // "must": [ - // { "term": { "language": "en" } }, - // { "term": { "subjects.keyword": "Lower Austria" } }, - // { "term": { "subjects.keyword": "counting data" } } - // ], - // "should": [ - // { match: { title: { query: "halger", fuzziness: "AUTO", boost: 3 } } }, - // { match: { author: { query: "halger", fuzziness: "AUTO", boost: 2 } } }, - // { match: { subjects: { query: "halger", fuzziness: "AUTO", boost: 1 } } }, - // { wildcard: { title: { value: "halger", boost: 3 } } }, - // { wildcard: { author: { value: "halger", boost: 2 } } }, - // { wildcard: { subjects: { value: "halger", boost: 1 } } } - // ], - // minimum_should_match: 1 - // } - // }, - - // size: 10, - // from: startValue, - // sort: [{ _score: { order: "desc" } }], - // track_scores: true, - - // aggs: { - // subjects: { terms: { field: "subjects.keyword", size: 1000 } }, - // language: { terms: { field: "language" } }, - // author: { terms: { field: "author.keyword", size: 1000 } }, - // year: { terms: { field: "year", size: 100 } } - // }, - - // highlight: { - // fields: { - // title: {}, - // author: {}, - // subjects: {} - // } - // } - // }; - - // console.log("mainQuery:", mainQuery); - // console.log("filters:", filters); - // console.log("body:", body); - - // // Make API call to OpenSearch and return the result - // const stations = api.post(base, body); - - // return stations; - // } - - - - public facetedSearchOPEN( + /** + * Perform faceted search with OpenSearch API using filters and suggestions + * @param {Suggestion | string} suggestion - Search term or suggestion + * @param {ActiveFilterCategories} activeFilterCategories - Active filters to apply + * @param {string} openCore - The OpenSearch core to search in + * @param {string} openHost - The OpenSearch host URL + * @param {string} start - Optional: starting page + * @returns {Observable} - Observable emitting search results + */ + public facetedSearch( suggestion: Suggestion | string, activeFilterCategories: ActiveFilterCategories, openCore: string, openHost: string, start?: string, // Starting page ): Observable { - // console.log("FACETEDSEARCH"); - - // OpenSearch endpoint const host = openHost; const path = "/" + openCore + "/_search"; const base = host + path; const lowercaseTerm = typeof suggestion === 'string' ? suggestion.toLowerCase() : suggestion.value; - // console.log("facetedsearchOPEN > suggestion entered:"); - // console.log(suggestion); - // console.log("typeof:", typeof suggestion); - - /** - * The query construction depends on whether the suggestion is a string or a Suggestion object. */ + * The query construction depends on whether the suggestion is a string or a Suggestion object. + * */ + // When suggestion is a string: const mainQuery = typeof suggestion === 'string' ? { @@ -350,7 +164,7 @@ class DatasetService { minimum_should_match: 1 } } - // When suggestion is a suggestion object + // When suggestion is a suggestion object: : { match: { [suggestion.type]: { @@ -360,28 +174,7 @@ class DatasetService { } }; - // CONTABO ==================================================== - // // Constructing Filters Based on Active Filter Categories - // const filters = Object.entries(activeFilterCategories).map(([category, values]) => ({ - // terms: { [`${category}.keyword`]: values } - // })); - // ================================================================ - - // // Constructing Filters Based on Active Filter Categories - // const filters = Object.entries(activeFilterCategories).map(([category, values]) => { - // if (category === "language" || category === "year") { - // return { terms: { [category]: values } }; - // } else { - // return { terms: { [`${category}.keyword`]: values } }; - // } - // }); - - - // const filters = Object.entries(activeFilterCategories).map(([category, values]) => - // values.map(value => ({ term: { [`${category}.keyword`]: value } })) - // ).flat(); - - + // Build filters based on the active filter categories const filters = Object.entries(activeFilterCategories).map(([category, values]) => { if (category === "language" || category === "year" || category === "doctype") { return values.map(value => ({ term: { [category]: value } })); @@ -390,22 +183,8 @@ class DatasetService { } }).flat(); - // console.log(activeFilterCategories); - console.log("mainQuery:", mainQuery); - console.log("filters:", filters); - + // Request body for the faceted search const body = { - // query: { - // bool: { - // must: query, // Contains the main query constructed earlier. - // filter: filters // Contains the filters constructed from activeFilterCategories. - // // filter: [ - // // { "terms": { "subjects.keyword": ["Lower Austria", "counting data"] } }, - // // { "term": { "language": "en" } } - // // ] // Contains the filters constructed from activeFilterCategories. - // } - // }, - query: { bool: { must: [ @@ -415,49 +194,24 @@ class DatasetService { } }, - // // THIS DOESNT WORK // Expected: 12 results, Ouput: 16 - // query: { - // bool: { - // "must": [ - // { "term": { "language": "en" } }, - // { "terms": { "subjects.keyword": ["Lower Austria", "counting data"] } } - // ], - // "should": [ - // { match: { title: { query: "halger", fuzziness: "AUTO", boost: 3 } } }, - // { match: { author: { query: "halger", fuzziness: "AUTO", boost: 2 } } }, - // { match: { subjects: { query: "halger", fuzziness: "AUTO", boost: 1 } } }, - // { wildcard: { title: { value: "halger", boost: 3 } } }, - // { wildcard: { author: { value: "halger", boost: 2 } } }, - // { wildcard: { subjects: { value: "halger", boost: 1 } } } - // ], - // minimum_should_match: 1 - // } - // }, - size: 10, from: start ? parseInt(start) : 0, - // sort: [{ _source: { server_date_published: { order: "desc" } } }], - sort: [{ server_date_published: { order: "desc" } }], + sort: [{ server_date_published: { order: "desc" } }], // Sort by publication date // sort: [{ _score: { order: "desc" } }], // Sort by _score in descending order track_scores: true, - aggs: { // Defines aggregations for facets - // terms: Aggregation type that returns the most common terms in a field. - // !For a large number of terms setting an extremely large size might not be efficient - // If you genuinely need all unique terms and expect a large number of them, consider using a composite aggregation for more efficient pagination of terms. + /** + * Defines aggregations for facets + * terms: Aggregation type that returns the most common terms in a field. + * !For a large number of terms setting an extremely large size might not be efficient + * If you genuinely need all unique terms and expect a large number of them, consider using a composite aggregation for more efficient pagination of terms. + */ + aggs: { subjects: { terms: { field: "subjects.keyword", size: 1000 } }, // In SOLR is "subject"! language: { terms: { field: "language" } }, // ".keyword" HAS TO BE REMOVED. OTHERWISE BUCKETS ARE NOT OBTAINED FOR THIS author: { terms: { field: "author.keyword", size: 1000 } }, year: { terms: { field: "year", size: 100 } }, // ".keyword" HAS TO BE REMOVED. OTHERWISE BUCKETS ARE NOT OBTAINED FOR THIS doctype: { terms: { field: "doctype", size: 50 } } // ".keyword" HAS TO BE REMOVED. OTHERWISE BUCKETS ARE NOT OBTAINED FOR THIS }, - // CONTABO ================================================================================ - // aggs: { - // subjects: { terms: { field: "subjects.keyword", size: 1000 } }, // In SOLR is "subject"! - // language: { terms: { field: "language.keyword" } }, // << ".keyword" HAS TO BE REMOVED. OTHERWISE BUCKETS ARE NOT OBTAINED FOR THIS - // author: { terms: { field: "author.keyword", size: 1000 } }, - // year: { terms: { field: "year.keyword", size: 100 } } // << ".keyword" HAS TO BE REMOVED. OTHERWISE BUCKETS ARE NOT OBTAINED FOR THIS - // }, - // =========================================================================================== highlight: { fields: { title: {}, @@ -468,17 +222,15 @@ class DatasetService { } }; - // console.log("body:", body); - + // API call and return observable of search results const stations = api.post(base, body); - return stations; } // /** // * This method performs a faceted search on a Solr core. Faceted search allows the user to filter search results based on various categories (facets) // */ - // public facetedSearch_SOLR( + // public facetedSearchSOLR( // suggestion: Suggestion | string, // activeFilterCategories: ActiveFilterCategories, // solrCore: string, @@ -609,8 +361,8 @@ class DatasetService { const host = VUE_API; const path = "/api/dataset/" + id; const apiUrl = host + path; - const dataset = api.get(apiUrl).pipe(map((res) => this.prepareDataset(res))); + const dataset = api.get(apiUrl).pipe(map((res) => this.prepareDataset(res))); return dataset; } @@ -619,16 +371,16 @@ class DatasetService { const host = VUE_API; const path = "/api/dataset/10.24341/tethys." + doi; const apiUrl = host + path; - const dataset = api.get(apiUrl).pipe(map((res) => this.prepareDataset(res))); + const dataset = api.get(apiUrl).pipe(map((res) => this.prepareDataset(res))); return dataset; } - // Method to prepare dataset object + // Prepare dataset object by deserializing it and adding a URL private prepareDataset(datasetObj: DbDataset): DbDataset { + const dataset = deserialize(DbDataset, JSON.stringify(datasetObj)); dataset.url = document.documentURI; - return dataset; } } diff --git a/src/views/search-view/search-view-component.ts b/src/views/search-view/search-view-component.ts index d8e666c..db7dd56 100644 --- a/src/views/search-view/search-view-component.ts +++ b/src/views/search-view/search-view-component.ts @@ -1,17 +1,18 @@ +// Import necessary modules, components, and models from Vue and the project import { Component, Vue, Prop } from "vue-facing-decorator"; import VsInput from "@/components/vs-input/vs-input.vue"; import VsResult from "@/components/vs-result/vs-result.vue"; import FacetCategory from "@/components/face-category/facet-category.vue"; import ActiveFacetCategory from "@/components/active-facet-category/active-facet-category.vue"; -// import { SolrSettings } from "@/models/solr"; +// Import models and services +// import { SolrSettings } from "@/models/solr"; import { OpenSettings } from "@/models/solr"; -// import { DatasetService } from "@/services/dataset.service"; import DatasetService from "../../services/dataset.service"; import { Suggestion, Dataset, SearchType } from "@/models/dataset"; // import { SolrResponse, FacetFields, FacetItem, FacetResults, FacetInstance } from "@/models/headers"; // import { SolrResponse, FacetFields, FacetItem, FacetResults, FacetInstance, OpenSearchResponse, HitHighlight } from "@/models/headers"; -import { FacetFields, FacetItem, FacetResults, FacetInstance, OpenSearchResponse, HitHighlight } from "@/models/headers"; +import { FacetItem, FacetResults, OpenSearchResponse } from "@/models/headers"; import { ActiveFilterCategories } from "@/models/solr"; // import { SOLR_HOST, SOLR_CORE } from "@/constants"; import { IPagination } from "@/models/pagination"; @@ -19,7 +20,7 @@ import PaginationComponent from "@/components/PaginationComponent.vue"; import { OPEN_HOST, OPEN_CORE } from "@/constants"; -// Decorate the component and define its name and components +// Define the Vue component, its name, and child components @Component({ name: "SearchViewComponent", components: { @@ -31,51 +32,51 @@ import { OPEN_HOST, OPEN_CORE } from "@/constants"; }, }) -// Define the SearchViewComponent class +// Export the default class for the component export default class SearchViewComponent extends Vue { - @Prop() - display!: string; - - @Prop() - type!: string; - - results: Array = []; - - // facets: FacetFields = new FacetFields(); - facets: FacetResults = new FacetResults(); - searchTerm: string | Suggestion = ""; - // activeFilterCategories: Object = {}; - activeFilterCategories: ActiveFilterCategories = new ActiveFilterCategories(); // = new Array(); - pagination: IPagination = { + // Define props passed from the parent component + @Prop() + display!: string; // Search display string + @Prop() + type!: string; // Search type + + // Declare variables used in the component + results: Array = []; // Array to hold search results + facets: FacetResults = new FacetResults(); // Object to hold facet results + searchTerm: string | Suggestion = ""; // The search term input + activeFilterCategories: ActiveFilterCategories = new ActiveFilterCategories(); // Active filter categories for search + pagination: IPagination = { // Pagination data for the results total: 0, perPage: 10, currentPage: 1, - // lastPage: 0, data: [], }; - loaded = false; - numFound!: number; + loaded = false; // Boolean to track whether data has been loaded + numFound!: number; // Number of results found + // private solr: SolrSettings = { // core: SOLR_CORE, //"rdr_data", // SOLR.core; // host: SOLR_HOST, //"tethys.at", // }; + // Define settings for the OpenSearch API (core and host information) private open: OpenSettings = { - core: OPEN_CORE, //"rdr_data", // SOLR.core; - host: OPEN_HOST, //"tethys.at", + core: OPEN_CORE, // + host: OPEN_HOST, // }; private error = ""; // Computed property to get search term as string get stringSearchTerm(): string { - // console.log("stringSearchTerm:", this.searchTerm); - + // If searchTerm is a string, return it directly if (typeof this.searchTerm === "string") { return this.searchTerm; + // If searchTerm is a Suggestion, return its value and type alias } else if (this.searchTerm instanceof Suggestion) { return this.searchTerm.value + " (" + this.getTypeAlias(this.searchTerm.type) + ")"; // return this.searchTerm.value + " (" + this.searchTerm.type + ")"; + // Default to empty string } else { return ""; } @@ -109,9 +110,6 @@ export default class SearchViewComponent extends Vue { return false; } } - // getKeyName(value: string) { - // return Object.entries(Suggestion).find(([key, val]) => val === value)?.[0]; - // } // Method to get enum key by enum value getEnumKeyByEnumValue(myEnum: T, enumValue: string): keyof T | null { @@ -124,7 +122,6 @@ export default class SearchViewComponent extends Vue { beforeMount(): void { // console.log("beforeMount!"); - // this.rdrAPI = new DatasetService(); // Trigger search based on provided display and type props if (this.display != "" && this.type != undefined) { const enumKey: "Title" | "Author" | "Subject" | "Doctype" | null = this.getEnumKeyByEnumValue(SearchType, this.type); @@ -150,64 +147,30 @@ export default class SearchViewComponent extends Vue { this.activeFilterCategories = new ActiveFilterCategories(); this.facets = new FacetResults(); this.searchTerm = suggestion; - // console.log("ONSEARCH > suggestion: ", suggestion); // /* 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 */ - // DatasetService.facetedSearch(suggestion, this.activeFilterCategories, this.solr.core, this.solr.host, undefined).subscribe({ + // DatasetService.facetedSearchSOLR(suggestion, this.activeFilterCategories, this.solr.core, this.solr.host, undefined).subscribe({ // next: (res: SolrResponse) => this.dataHandler(res), // error: (error: string) => this.errorHandler(error), // }); - DatasetService.facetedSearchOPEN(suggestion, this.activeFilterCategories, this.open.core, this.open.host, undefined).subscribe({ - // next: (res: { datasets: Dataset[], highlights: HitHighlight[] }) => this.dataHandlerOpen(res.datasets, res.highlights), - next: (res: OpenSearchResponse) => this.dataHandlerOPEN(res), + DatasetService.facetedSearch(suggestion, this.activeFilterCategories, this.open.core, this.open.host, undefined).subscribe({ + next: (res: OpenSearchResponse) => this.dataHandler(res), error: (error: string) => this.errorHandler(error), }); } // Handle the search results - private dataHandlerOPEN(res: OpenSearchResponse, filterItem?: FacetItem): void { + private dataHandler(res: OpenSearchResponse, filterItem?: FacetItem): void { this.results = res.hits.hits.map(hit => hit._source); this.numFound = res.hits.total.value; - - // console.log("dataHandlerOPEN (results, numFound):"); - // console.log(this.results); - // console.log(this.numFound); - // console.log(res.hits.hits); - // console.log(res.hits.total.value); console.log(res); - // console.log("results:"); - // console.log(res); - - // for (const key in this.results) { - // if (Object.prototype.hasOwnProperty.call(this.results, key)) { - // const element = this.results[key]; - // // console.log(element.abstract[0]); - // // console.log(element.language); - // console.log(element.server_date_published); - // } - // } - this.pagination.total = res.hits.total.value; this.pagination.perPage = 10; this.pagination.data = this.results; this.pagination.lastPage = Math.ceil(this.pagination.total / this.pagination.perPage); - // if (res.aggregations) { - // const facet_fields = res.aggregations; - - // let prop: keyof typeof facet_fields; - - // for (prop in facet_fields) { - // const facetCategory = facet_fields[prop]; - // if (facetCategory.buckets) { - // const facetItems = facetCategory.buckets.map(bucket => new FacetItem(bucket.key, bucket.doc_count)); - // this.facets[prop] = facetItems.filter(el => el.count > 0); - // } - // } - // } - if (res.aggregations) { const facet_fields = res.aggregations; @@ -244,7 +207,7 @@ export default class SearchViewComponent extends Vue { } // // Method to handle search response - // private dataHandler(res: SolrResponse, filterItem?: FacetItem): void { + // private dataHandlerSOLR(res: SolrResponse, filterItem?: FacetItem): void { // // console.log("dataHandlerSOLR (docs, numFound):"); // // console.log(res.response.docs); // // console.log(res.response.numFound); @@ -315,13 +278,13 @@ export default class SearchViewComponent extends Vue { const start = page * this.pagination.perPage - this.pagination.perPage; // // Trigger new search with updated pagination parameters - // DatasetService.facetedSearch(this.searchTerm, this.activeFilterCategories, this.solr.core, this.solr.host, start.toString()).subscribe( + // DatasetService.facetedSearchSOLR(this.searchTerm, this.activeFilterCategories, this.solr.core, this.solr.host, start.toString()).subscribe( // (res: SolrResponse) => this.dataHandler(res), // (error: string) => this.errorHandler(error), // ); - DatasetService.facetedSearchOPEN(this.searchTerm, this.activeFilterCategories, this.open.core, this.open.host, start.toString()).subscribe({ - next: (res: OpenSearchResponse) => this.dataHandlerOPEN(res), + DatasetService.facetedSearch(this.searchTerm, this.activeFilterCategories, this.open.core, this.open.host, start.toString()).subscribe({ + next: (res: OpenSearchResponse) => this.dataHandler(res), error: (error: string) => this.errorHandler(error), }); } @@ -332,37 +295,32 @@ export default class SearchViewComponent extends Vue { // Reset current page this.pagination.currentPage = 1; - // console.log(facetItem.val); - // console.log(facetItem.category); - - // if (!this.activeFilterCategories.hasOwnProperty(facetItem.category)) { - + // Check if filter item already exists if (!Object.prototype.hasOwnProperty.call(this.activeFilterCategories, facetItem.category)) { this.activeFilterCategories[facetItem.category] = new Array(); - // console.log(this.activeFilterCategories); } - // if (!this.activeFilterCategories[facetItem.category].some((e) => e === facetItem.val)) { // Check if filter item is not already applied if (!this.activeFilterCategories[facetItem.category].some((e) => e === facetItem.val)) { // Add filter item to active filter categories this.activeFilterCategories[facetItem.category].push(facetItem.val); - // Trigger new search with updated filter - // DatasetService.facetedSearch(this.searchTerm, this.activeFilterCategories, this.solr.core, this.solr.host, undefined).subscribe( + + // DatasetService.facetedSearchSOLR(this.searchTerm, this.activeFilterCategories, this.solr.core, this.solr.host, undefined).subscribe( // (res: SolrResponse) => this.dataHandler(res, facetItem), // (error: string) => this.errorHandler(error), // ); - // console.log(this.activeFilterCategories); - DatasetService.facetedSearchOPEN(this.searchTerm, this.activeFilterCategories, this.open.core, this.open.host, undefined).subscribe({ - next: (res: OpenSearchResponse) => this.dataHandlerOPEN(res, facetItem), + + // Trigger new search with updated filter + DatasetService.facetedSearch(this.searchTerm, this.activeFilterCategories, this.open.core, this.open.host, undefined).subscribe({ + next: (res: OpenSearchResponse) => this.dataHandler(res, facetItem), error: (error: string) => this.errorHandler(error), }); } } // // // Method to clear facet category filter - // onClearFacetCategory(categoryName: string): void { + // onClearFacetCategorySOLR(categoryName: string): void { // console.log("onClearFacetCategory"); // delete this.activeFilterCategories[categoryName]; @@ -416,12 +374,12 @@ export default class SearchViewComponent extends Vue { // } // Method to clear facet category filter - onClearFacetCategoryOPEN(categoryName: string): void { - console.log("onClearFacetCategory"); + onClearFacetCategory(categoryName: string): void { + // console.log("onClearFacetCategory"); delete this.activeFilterCategories[categoryName]; // Trigger new search with updated filter - DatasetService.facetedSearchOPEN(this.searchTerm, this.activeFilterCategories, this.open.core, this.open.host, undefined).subscribe({ + DatasetService.facetedSearch(this.searchTerm, this.activeFilterCategories, this.open.core, this.open.host, undefined).subscribe({ next: (res: OpenSearchResponse) => { this.results = res.hits.hits.map(hit => hit._source); this.numFound = res.hits.total.value; diff --git a/src/views/search-view/search-view-component.vue b/src/views/search-view/search-view-component.vue index f2eeb8f..5fe366c 100644 --- a/src/views/search-view/search-view-component.vue +++ b/src/views/search-view/search-view-component.vue @@ -105,7 +105,7 @@