CTS link opens in new window. New comments on search related classes and functions

This commit is contained in:
Porras-Bernardez 2024-05-10 15:01:57 +02:00
parent d50bf55fb3
commit d50e93a658
3 changed files with 70 additions and 11 deletions

View File

@ -10,7 +10,7 @@
<a class="navbar-item" href="/"> <a class="navbar-item" href="/">
<!-- <img src="./assets/images/TETHYS-Logo.svg" width="240px" height="86" alt="TETHYS Logo" /> --> <!-- <img src="./assets/images/TETHYS-Logo.svg" width="240px" height="86" alt="TETHYS Logo" /> -->
<img src="./assets/images/TETHYS-Logo.svg" width="240" height="86" /> &nbsp;&nbsp; <img src="./assets/images/TETHYS-Logo.svg" width="240" height="86" /> &nbsp;&nbsp;
<a href="https://doi.org/10.34894/TKWVFL"><img src="./assets/images/cts-logo.png" width="80" height="80" /></a> <a href="https://doi.org/10.34894/TKWVFL" target="_blank"><img src="./assets/images/cts-logo.png" width="80" height="80" /></a>
</a> </a>
<a <a
id="menu-icon" id="menu-icon"

View File

@ -16,7 +16,7 @@ class DatasetService {
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 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
*/ */
// for the autocomplete search // For the autocomplete search. Method to perform a search based on a term
public searchTerm(term: string, solrCore: string, solrHost: string): Observable<Dataset[]> { public searchTerm(term: string, solrCore: string, solrHost: string): Observable<Dataset[]> {
// solr endpoint // solr endpoint
const host = "https://" + solrHost; const host = "https://" + solrHost;
@ -48,13 +48,14 @@ class DatasetService {
wt: "json", wt: "json",
}; };
// Make API call to Solr and return the result
const stations = api.get<SolrResponse>(base, q_params).pipe(map((res: SolrResponse) => res.response.docs)); const stations = api.get<SolrResponse>(base, q_params).pipe(map((res: SolrResponse) => res.response.docs));
return stations; return stations;
} }
/* Only one facet => Author: Coric, Stjepan (16) /* E.g. Only one facet => Author: Coric, Stjepan (16)
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&fq=author%3A%28%22Coric%2C%20Stjepan%22%29&start=0&sort=server_date_published%20desc&facet=on &q.op=or&defType=edismax&qf=title%5E3%20author%5E2%20subject%5E1&indent=on&wt=json&rows=10&fq=author%3A%28%22Coric%2C%20Stjepan%22%29&start=0&sort=server_date_published%20desc&facet=on
&json.facet.language=%7B%20type%3A%20%22terms%22%2C%20field%3A%20%22language%22%20%7D &json.facet.language=%7B%20type%3A%20%22terms%22%2C%20field%3A%20%22language%22%20%7D
@ -62,6 +63,7 @@ class DatasetService {
&json.facet.year=%7B%20type%3A%20%22terms%22%2C%20field%3A%20%22year%22%20%7D &json.facet.year=%7B%20type%3A%20%22terms%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 */ &json.facet.author=%7B%20type%3A%20%22terms%22%2C%20field%3A%20%22author_facet%22%2C%20limit%3A%20-1%20%7D */
// Method to perform a faceted search
public facetedSearch( public facetedSearch(
suggestion: Suggestion | string, suggestion: Suggestion | string,
activeFilterCategories: ActiveFilterCategories, activeFilterCategories: ActiveFilterCategories,
@ -69,6 +71,7 @@ class DatasetService {
solrHost: string, solrHost: string,
start?: string, // Starting page start?: string, // Starting page
): Observable<SolrResponse> { ): Observable<SolrResponse> {
// Construct Solr query parameters
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;
@ -86,6 +89,7 @@ class DatasetService {
"doctype", "doctype",
].toString(); ].toString();
// Determine search term, query operator, and query fields based on the suggestion type
let term, queryOperator, qfFields; let term, queryOperator, qfFields;
if (typeof suggestion === "string") { if (typeof suggestion === "string") {
term = suggestion + "*"; term = suggestion + "*";
@ -97,10 +101,11 @@ class DatasetService {
qfFields = undefined; qfFields = undefined;
} }
// Set default value for start if not provided
if (start === undefined) start = "0"; if (start === undefined) start = "0";
// Construct filter fields based on active filter categories
const filterFields = new Array<string>(); const filterFields = new Array<string>();
if (Object.keys(activeFilterCategories).length > 0) { if (Object.keys(activeFilterCategories).length > 0) {
/* Declare variable prop with a type that is a key of the activeFilterCategories. The 'keyof typeof' activeFilterCategories type represents all possible keys /* Declare variable prop with a type that is a key of the activeFilterCategories. The 'keyof typeof' activeFilterCategories type represents all possible keys
that can exist on the activeFilterCategories --> prop can only be assigned a value that is a key of the activeFilterCategories object */ that can exist on the activeFilterCategories --> prop can only be assigned a value that is a key of the activeFilterCategories object */
@ -109,11 +114,13 @@ class DatasetService {
const filterItems = activeFilterCategories[prop]; const filterItems = activeFilterCategories[prop];
filterItems.forEach(function (value: string) { filterItems.forEach(function (value: string) {
filterFields.push(prop + ':("' + value + '")'); filterFields.push(prop + ':("' + value + '")');
// e.g. Array [ 'subject:("Vektordaten")', 'author:("GeoSphere Austria, ")' ]
}); });
} }
} }
// https://solr.apache.org/guide/8_4/json-request-api.html // https://solr.apache.org/guide/8_4/json-request-api.html
// Construct Solr query parameters
const q_params = { const q_params = {
"0": "fl=" + fields, "0": "fl=" + fields,
q: term, q: term,
@ -135,12 +142,26 @@ class DatasetService {
"json.facet.year": '{ type: "terms", field: "year" }', "json.facet.year": '{ type: "terms", field: "year" }',
"json.facet.author": '{ type: "terms", field: "author_facet", limit: -1 }', "json.facet.author": '{ type: "terms", field: "author_facet", limit: -1 }',
}; };
/* E.g.
{"0":"fl=id,licence,server_date_published,abstract_output,identifier,title_output,title_additional,author,subject,doctype","q":"*","q.op":"or","defType":"edismax",
"qf":"title^3 author^2 subject^1",
"indent":"on","wt":"json","rows":10,
"fq":["subject:(\"Vektordaten\")","author:(\"GeoSphere Austria, \")"],
"start":"0","sort":"server_date_published desc","facet":"on",
"json.facet.language":"{ type: \"terms\", field: \"language\" }",
"json.facet.subject":"{ type: \"terms\", field: \"subject\", limit: -1 }",
"json.facet.year":"{ type: \"terms\", field: \"year\" }",
"json.facet.author":"{ type: \"terms\", field: \"author_facet\", limit: -1 }"}
*/
// console.log(JSON.stringify(q_params));
// Make API call to Solr and return the result
const stations = api.get<SolrResponse>(base, q_params); const stations = api.get<SolrResponse>(base, q_params);
return stations; return stations;
} }
// Method to fetch years
public getYears(): Observable<string[]> { public getYears(): Observable<string[]> {
const host = VUE_API; const host = VUE_API;
const path = "/api/years"; const path = "/api/years";
@ -150,6 +171,7 @@ class DatasetService {
return years; return years;
} }
// Method to fetch documents for a specific year
public getDocuments(year: string): Observable<Array<DbDataset>> { public getDocuments(year: string): Observable<Array<DbDataset>> {
const host = VUE_API; const host = VUE_API;
const path = "/api/sitelinks/" + year; const path = "/api/sitelinks/" + year;
@ -159,6 +181,7 @@ class DatasetService {
return documents; return documents;
} }
// Method to fetch a dataset by its ID
public getDataset(id: number): Observable<DbDataset> { public getDataset(id: number): Observable<DbDataset> {
const host = VUE_API; const host = VUE_API;
const path = "/api/dataset/" + id; const path = "/api/dataset/" + id;
@ -168,6 +191,7 @@ class DatasetService {
return dataset; return dataset;
} }
// Method to fetch a dataset by its DOI
public getDatasetByDoi(doi: string): Observable<DbDataset> { public getDatasetByDoi(doi: string): Observable<DbDataset> {
const host = VUE_API; const host = VUE_API;
const path = "/api/dataset/10.24341/tethys." + doi; const path = "/api/dataset/10.24341/tethys." + doi;
@ -177,6 +201,7 @@ class DatasetService {
return dataset; return dataset;
} }
// Method to prepare dataset object
private prepareDataset(datasetObj: DbDataset): DbDataset { private prepareDataset(datasetObj: DbDataset): DbDataset {
const dataset = deserialize<DbDataset>(DbDataset, JSON.stringify(datasetObj)); const dataset = deserialize<DbDataset>(DbDataset, JSON.stringify(datasetObj));
dataset.url = document.documentURI; dataset.url = document.documentURI;

View File

@ -13,6 +13,7 @@ import { SOLR_HOST, SOLR_CORE } from "@/constants";
import { IPagination } from "@/models/pagination"; import { IPagination } from "@/models/pagination";
import PaginationComponent from "@/components/PaginationComponent.vue"; import PaginationComponent from "@/components/PaginationComponent.vue";
// Decorate the component and define its name and components
@Component({ @Component({
name: "SearchViewComponent", name: "SearchViewComponent",
components: { components: {
@ -23,6 +24,8 @@ import PaginationComponent from "@/components/PaginationComponent.vue";
PaginationComponent, PaginationComponent,
}, },
}) })
// Define the SearchViewComponent class
export default class SearchViewComponent extends Vue { export default class SearchViewComponent extends Vue {
@Prop() @Prop()
display!: string; display!: string;
@ -53,6 +56,7 @@ export default class SearchViewComponent extends Vue {
private error = ""; private error = "";
// Computed property to get search term as string
get stringSearchTerm(): string { get stringSearchTerm(): string {
if (typeof this.searchTerm === "string") { if (typeof this.searchTerm === "string") {
return this.searchTerm; return this.searchTerm;
@ -63,6 +67,7 @@ export default class SearchViewComponent extends Vue {
} }
} }
// Method to check if a search term is present
hasSearchTerm(): boolean { hasSearchTerm(): boolean {
if (typeof this.searchTerm === "string" && this.searchTerm !== "") { if (typeof this.searchTerm === "string" && this.searchTerm !== "") {
return true; return true;
@ -75,14 +80,18 @@ export default class SearchViewComponent extends Vue {
// getKeyName(value: string) { // getKeyName(value: string) {
// return Object.entries(Suggestion).find(([key, val]) => val === value)?.[0]; // return Object.entries(Suggestion).find(([key, val]) => val === value)?.[0];
// } // }
// Method to get enum key by enum value
getEnumKeyByEnumValue<T extends { [index: string]: string }>(myEnum: T, enumValue: string): keyof T | null { getEnumKeyByEnumValue<T extends { [index: string]: string }>(myEnum: T, enumValue: string): keyof T | null {
const keys = Object.keys(myEnum).filter((x) => myEnum[x] == enumValue); const keys = Object.keys(myEnum).filter((x) => myEnum[x] == enumValue);
return keys.length > 0 ? keys[0] : null; return keys.length > 0 ? keys[0] : null;
// return keys[0]; // return keys[0];
} }
// Lifecycle hook: executed before the component is mounted
beforeMount(): void { beforeMount(): void {
// this.rdrAPI = new DatasetService(); // this.rdrAPI = new DatasetService();
// Trigger search based on provided display and type props
if (this.display != "" && this.type != undefined) { if (this.display != "" && this.type != undefined) {
const enumKey: "Title" | "Author" | "Subject" | null = this.getEnumKeyByEnumValue(SearchType, this.type); const enumKey: "Title" | "Author" | "Subject" | null = this.getEnumKeyByEnumValue(SearchType, this.type);
if (enumKey) { if (enumKey) {
@ -98,12 +107,14 @@ export default class SearchViewComponent extends Vue {
} }
} }
// Method to trigger a search
onSearch(suggestion: Suggestion | string): void { onSearch(suggestion: Suggestion | string): void {
// 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;
/* 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({
next: (res: SolrResponse) => this.dataHandler(res), next: (res: SolrResponse) => this.dataHandler(res),
@ -111,12 +122,13 @@ export default class SearchViewComponent extends Vue {
}); });
} }
// Method to handle search response
private dataHandler(res: SolrResponse, filterItem?: FacetItem): void { private dataHandler(res: SolrResponse, filterItem?: FacetItem): void {
// Update results
this.results = res.response.docs; this.results = res.response.docs;
this.numFound = res.response.numFound; this.numFound = res.response.numFound;
// pagination // Update pagination
this.pagination["total"] = res.response.numFound; this.pagination["total"] = res.response.numFound;
this.pagination["perPage"] = res.responseHeader.params.rows as number; this.pagination["perPage"] = res.responseHeader.params.rows as number;
this.pagination["data"] = res.response.docs; this.pagination["data"] = res.response.docs;
@ -124,8 +136,11 @@ export default class SearchViewComponent extends Vue {
const facet_fields: FacetFields = res.facets; const facet_fields: FacetFields = res.facets;
/* This code declares a variable prop with a type of keys of the facet_fields object. The keyof typeof facet_fields type represents the keys of the facet_fields object.
This means that prop can only hold values that are keys of the facet_fields object. */
let prop: keyof typeof facet_fields; let prop: keyof typeof facet_fields;
// Iterate through facet fields
for (prop in facet_fields) { for (prop in facet_fields) {
const facetCategory = facet_fields[prop]; const facetCategory = facet_fields[prop];
if (facetCategory.buckets) { if (facetCategory.buckets) {
@ -133,55 +148,70 @@ export default class SearchViewComponent extends Vue {
let facetValues = facetItems.map((facetItem) => { let facetValues = facetItems.map((facetItem) => {
let rObj: FacetItem; let rObj: FacetItem;
// Check if current facet item matches filter item
if (filterItem?.val == facetItem.val) { if (filterItem?.val == facetItem.val) {
rObj = filterItem; rObj = filterItem;
} else if (this.facets[prop]?.some((e) => e.val === facetItem.val)) { } else if (this.facets[prop]?.some((e) => e.val === facetItem.val)) {
// console.log(facetValue + " is included") // console.log(facetValue + " is included")
// Update existing facet item with new count
const indexOfFacetValue = this.facets[prop].findIndex((i) => i.val === facetItem.val); const indexOfFacetValue = this.facets[prop].findIndex((i) => i.val === facetItem.val);
// console.log(indexOfFacetValue); // console.log(indexOfFacetValue);
rObj = this.facets[prop][indexOfFacetValue]; rObj = this.facets[prop][indexOfFacetValue];
rObj.count = facetItem.count; rObj.count = facetItem.count;
// rObj = new FacetItem(val, count); // rObj = new FacetItem(val, count);
} else { } else {
// Create new facet item
rObj = new FacetItem(facetItem.val, facetItem.count); rObj = new FacetItem(facetItem.val, facetItem.count);
} }
return rObj; return rObj;
}); });
// Filter out null values and values with count <= 0
facetValues = facetValues.filter(function (el) { facetValues = facetValues.filter(function (el) {
return el != null && el.count > 0; return el != null && el.count > 0;
}); });
// this.facets[prop] = facetCategory; // this.facets[prop] = facetCategory;
// Update facet values
this.facets[prop] = facetValues; this.facets[prop] = facetValues;
} }
} }
} }
// Method to handle search errors
private errorHandler(err: string): void { private errorHandler(err: string): void {
this.error = err; this.error = err;
} }
// Method to handle pagination
onMenuClick(page: number) { onMenuClick(page: number) {
this.pagination.currentPage = page; this.pagination.currentPage = page;
const start = page * this.pagination.perPage - this.pagination.perPage; 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.facetedSearch(this.searchTerm, this.activeFilterCategories, this.solr.core, this.solr.host, start.toString()).subscribe(
(res: SolrResponse) => this.dataHandler(res), (res: SolrResponse) => this.dataHandler(res),
(error: string) => this.errorHandler(error), (error: string) => this.errorHandler(error),
); );
} }
// Method to handle facet filtering
onFilter(facetItem: FacetItem): void { onFilter(facetItem: FacetItem): void {
// Reset current page
this.pagination.currentPage = 1; this.pagination.currentPage = 1;
// console.log(facetItem.val); // console.log(facetItem.val);
// if (!this.activeFilterCategories.hasOwnProperty(facetItem.category)) { // if (!this.activeFilterCategories.hasOwnProperty(facetItem.category)) {
// Check if filter item already exists
if (!Object.prototype.hasOwnProperty.call(this.activeFilterCategories, facetItem.category)) { if (!Object.prototype.hasOwnProperty.call(this.activeFilterCategories, facetItem.category)) {
this.activeFilterCategories[facetItem.category] = new Array<string>(); this.activeFilterCategories[facetItem.category] = new Array<string>();
} }
// if (!this.activeFilterCategories[facetItem.category].some((e) => e === facetItem.val)) { // if (!this.activeFilterCategories[facetItem.category].some((e) => e === facetItem.val)) {
if (!this.activeFilterCategories[facetItem.category].some((e) => e === facetItem.val)) {
this.activeFilterCategories[facetItem.category].push(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.facetedSearch(this.searchTerm, this.activeFilterCategories, this.solr.core, this.solr.host, undefined).subscribe(
(res: SolrResponse) => this.dataHandler(res, facetItem), (res: SolrResponse) => this.dataHandler(res, facetItem),
(error: string) => this.errorHandler(error), (error: string) => this.errorHandler(error),
@ -189,9 +219,11 @@ export default class SearchViewComponent extends Vue {
} }
} }
// Method to clear facet category filter
onClearFacetCategory(categoryName: string): void { onClearFacetCategory(categoryName: string): void {
delete this.activeFilterCategories[categoryName]; delete this.activeFilterCategories[categoryName];
// Trigger new search with updated filter
DatasetService.facetedSearch(this.searchTerm, this.activeFilterCategories, this.solr.core, this.solr.host, undefined).subscribe({ DatasetService.facetedSearch(this.searchTerm, this.activeFilterCategories, this.solr.core, this.solr.host, undefined).subscribe({
next: (res: SolrResponse) => { next: (res: SolrResponse) => {
this.results = res.response.docs; this.results = res.response.docs;
@ -214,6 +246,7 @@ export default class SearchViewComponent extends Vue {
let rObj: FacetItem; let rObj: FacetItem;
if (this.facets[prop]?.some((e) => e.val === facetItem.val)) { if (this.facets[prop]?.some((e) => e.val === facetItem.val)) {
// console.log(facetValue + " is included") // console.log(facetValue + " is included")
// Update existing facet item with new count
const indexOfFacetValue = this.facets[prop].findIndex((i) => i.val === facetItem.val); const indexOfFacetValue = this.facets[prop].findIndex((i) => i.val === facetItem.val);
// console.log(indexOfFacetValue); // console.log(indexOfFacetValue);
rObj = this.facets[prop][indexOfFacetValue]; rObj = this.facets[prop][indexOfFacetValue];
@ -224,6 +257,7 @@ export default class SearchViewComponent extends Vue {
rObj.active = false; rObj.active = false;
} }
} else { } else {
// Create new facet item
rObj = new FacetItem(facetItem.val, facetItem.count); rObj = new FacetItem(facetItem.val, facetItem.count);
} }
return rObj; return rObj;