Merge Frank's branch 'dev.frontend/opensearch' into feat/opensearch

This commit is contained in:
Porras-Bernardez 2024-08-06 16:41:48 +02:00
commit cc62df68da
25 changed files with 1350 additions and 745 deletions

View File

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

9
OpenSearch queries.txt Normal file
View File

@ -0,0 +1,9 @@
Get all documents in the index ("core" in solr)
----------------------------------------------------
curl -XGET "http://192.168.21.18/tethys-records/_search?pretty" -H 'Content-Type: application/json' -d'
{
"query": {
"match_all": {}
}
}'

60
SOLR queries.txt Normal file
View File

@ -0,0 +1,60 @@
Search by a specific value for one of the fields
https://tethys.at/solr/rdr_data/select?&q=year:2024
Search within a specific range of values for one of the fields
https://tethys.at/solr/rdr_data/select?&q=year:2023%20TO%202024 years 2023 to 2024
Search for a term (search done in predefined field?)
https://tethys.at/solr/rdr_data/select?&q=linz
Predefined Tethys Search
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%22terms%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%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
Predefined Tethys search changing the last facet to "doctype"
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=*&q.op=or&defType=edismax&qf=title^3%20author^2%20subject^1&indent=on&wt=json&rows=10&start=0&sort=server_date_published%20desc&facet=on&json.facet.language={%20type%3A%20%22terms%22%2C%20field%3A%20%22language%22%20}&json.facet.subject={%20type%3A%20%22terms%22%2C%20field%3A%20%22subject%22%2C%20limit%3A%20-1%20}&json.facet.author={%20type%3A%20%22terms%22%2C%20field%3A%20%22author_facet%22%2C%20limit%3A%20-1%20}&json.facet.doctype={%20type%3A%20%22terms%22%2C%20field%3A%20%22doctype%22%2C%20limit%3A%20-1%20}
Giving a value for 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&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.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%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
ASCII
+----+-----+----+-----+----+-----+----+-----+
| Hx | Chr | Hx | Chr | Hx | Chr | Hx | Chr |
+----+-----+----+-----+----+-----+----+-----+
| 00 | NUL | 20 | SPC | 40 | @ | 60 | ` |
| 01 | SOH | 21 | ! | 41 | A | 61 | a |
| 02 | STX | 22 | " | 42 | B | 62 | b |
| 03 | ETX | 23 | # | 43 | C | 63 | c |
| 04 | EOT | 24 | $ | 44 | D | 64 | d |
| 05 | ENQ | 25 | % | 45 | E | 65 | e |
| 06 | ACK | 26 | & | 46 | F | 66 | f |
| 07 | BEL | 27 | ' | 47 | G | 67 | g |
| 08 | BS | 28 | ( | 48 | H | 68 | h |
| 09 | TAB | 29 | ) | 49 | I | 69 | i |
| 0A | LF | 2A | * | 4A | J | 6A | j |
| 0B | VT | 2B | + | 4B | K | 6B | k |
| 0C | FF | 2C | , | 4C | L | 6C | l |
| 0D | CR | 2D | - | 4D | M | 6D | m |
| 0E | SO | 2E | . | 4E | N | 6E | n |
| 0F | SI | 2F | / | 4F | O | 6F | o |
| 10 | DLE | 30 | 0 | 50 | P | 70 | p |
| 11 | DC1 | 31 | 1 | 51 | Q | 71 | q |
| 12 | DC2 | 32 | 2 | 52 | R | 72 | r |
| 13 | DC3 | 33 | 3 | 53 | S | 73 | s |
| 14 | DC4 | 34 | 4 | 54 | T | 74 | t |
| 15 | NAK | 35 | 5 | 55 | U | 75 | u |
| 16 | SYN | 36 | 6 | 56 | V | 76 | v |
| 17 | ETB | 37 | 7 | 57 | W | 77 | w |
| 18 | CAN | 38 | 8 | 58 | X | 78 | x |
| 19 | EM | 39 | 9 | 59 | Y | 79 | y |
| 1A | SUB | 3A | : | 5A | Z | 7A | z |
| 1B | ESC | 3B | ; | 5B | [ | 7B | { |
| 1C | FS | 3C | < | 5C | \ | 7C | | |
| 1D | GS | 3D | = | 5D | ] | 7D | } |
| 1E | RS | 3E | > | 5E | ^ | 7E | ~ |
| 1F | US | 3F | ? | 5F | _ | 7F | DEL |
+----+-----+----+-----+----+-----+----+-----+
https://www.asciitable.com/ see Hx
If you write encodeURIComponent(",") in your JavaScript console, then you will also get %2C. And with decodeURIComponent("%2C") you will get back the ","

22
package-lock.json generated
View File

@ -14,6 +14,7 @@
"axios": "^1.2.2",
"class-transformer": "^0.5.1",
"dayjs": "^1.10.7",
"dompurify": "^3.1.5",
"leaflet": "^1.7.1",
"qs": "^6.10.1",
"rxjs": "^7.5.5",
@ -29,6 +30,7 @@
"@babel/plugin-proposal-decorators": "^7.22.5",
"@babel/preset-env": "^7.22.5",
"@tailwindcss/forms": "^0.5.7",
"@types/dompurify": "^3.0.5",
"@types/leaflet": "^1.7.9",
"@typescript-eslint/eslint-plugin": "^7.2.0",
"@typescript-eslint/parser": "^7.2.0",
@ -2497,6 +2499,15 @@
"@types/node": "*"
}
},
"node_modules/@types/dompurify": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz",
"integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==",
"dev": true,
"dependencies": {
"@types/trusted-types": "*"
}
},
"node_modules/@types/eslint": {
"version": "8.56.7",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.7.tgz",
@ -2694,6 +2705,12 @@
"@types/node": "*"
}
},
"node_modules/@types/trusted-types": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
"dev": true
},
"node_modules/@types/webpack-env": {
"version": "1.18.4",
"resolved": "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.18.4.tgz",
@ -5626,6 +5643,11 @@
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
"node_modules/dompurify": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.5.tgz",
"integrity": "sha512-lwG+n5h8QNpxtyrJW/gJWckL+1/DQiYMX8f7t8Z2AZTPw1esVrqjI63i7Zc2Gz0aKzLVMYC1V1PL/ky+aY/NgA=="
},
"node_modules/domutils": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",

View File

@ -17,6 +17,7 @@
"axios": "^1.2.2",
"class-transformer": "^0.5.1",
"dayjs": "^1.10.7",
"dompurify": "^3.1.5",
"leaflet": "^1.7.1",
"qs": "^6.10.1",
"rxjs": "^7.5.5",
@ -32,6 +33,7 @@
"@babel/plugin-proposal-decorators": "^7.22.5",
"@babel/preset-env": "^7.22.5",
"@tailwindcss/forms": "^0.5.7",
"@types/dompurify": "^3.0.5",
"@types/leaflet": "^1.7.9",
"@typescript-eslint/eslint-plugin": "^7.2.0",
"@typescript-eslint/parser": "^7.2.0",

View File

@ -34,7 +34,7 @@
</li>
<li class="navbar-item">
<!-- <a class="navbar-link is-arrowless" href="#">SEARCH</a> -->
<router-link class="navbar-link is-arrowless" to="/search">SEARCH</router-link>
<router-link class="navbar-link is-arrowless" to="/search">OPENSEARCH</router-link>
</li>
<li class="navbar-item">
<!-- <a class="navbar-link is-arrowless" href="#">SERVICES</a> -->

View File

@ -1,46 +1,37 @@
import initializeAxios from "./axiosSetup";
import { axiosRequestConfiguration } from "./config";
import { map } from "rxjs/operators";
// import { Observable } from "@reactivex/rxjs/compat";
import { defer, Observable } from "rxjs";
import { AxiosResponse } from "axios";
// https://ichi.pro/de/so-wickeln-sie-axios-mit-typescript-und-react-in-rxjs-ein-118892823169891
// Import the necessary modules and functions
import initializeAxios from "./axiosSetup"; // Function to initialize the Axios instance
import { axiosRequestConfiguration } from "./config"; // Axios configuration settings
import { map } from "rxjs/operators"; // Operator to transform the items emitted by an Observable
import { defer, Observable } from "rxjs"; // RxJS utilities for creating and working with Observables
import { AxiosResponse } from "axios"; // Axios response type
// Initialize the Axios instance with the provided configuration
const axiosInstance = initializeAxios(axiosRequestConfiguration);
// Function to make a GET request using Axios wrapped in an Observable
// eslint-disable-next-line
const get = <T>(url: string, queryParams?: any): Observable<T> => {
return defer(() => axiosInstance.get<T>(url, { params: queryParams })).pipe(map((result: AxiosResponse) => result.data));
// Use defer to create an Observable that makes the Axios GET request when subscribed to
return defer(() => axiosInstance.get<T>(url, { params: queryParams }))
// Use map to transform the Axios response to extract the data property
.pipe(map((result: AxiosResponse) => result.data));
};
// const post = <T>(url: string, body: object, queryParams?: any): Observable<T | void> => {
// return defer(() => axiosInstance.post<T>(url, body, { params: queryParams })).pipe(map((result: AxiosResponse) => result.data));
// };
// Function to make a POST request using Axios wrapped in an Observable
const post = <T>(url: string, body: any, queryParams?: any): Observable<T> => {
// Use defer to create an Observable that makes the Axios POST request when subscribed to
// console.log(body);
// console.log(queryParams);
return defer(() => axiosInstance.post<T>(url, body, {
params: queryParams,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
}))
.pipe(map((result: AxiosResponse) => result.data)); // Use map to transform the Axios response to extract the data property
};
// const put = <T>(
// url: string,
// body: object,
// queryParams?: object
// ): Observable<T | void> => {
// return defer(() =>
// axiosInstance.put<T>(url, body, { params: queryParams })
// ).pipe(map((result) => result.data));
// };
// const patch = <T>(
// url: string,
// body: object,
// queryParams?: object
// ): Observable<T | void> => {
// return defer(() =>
// axiosInstance.patch<T>(url, body, { params: queryParams })
// ).pipe(map((result) => result.data));
// };
// const deleteR = <T>(url: string, id: number): Observable<T | void> => {
// return defer(() => axiosInstance.delete(`${url}/${id}`)).pipe(
// map((result) => result.data)
// );
// };
export default { get };
// Export the get and post functions as part of the default export
export default { get, post };

View File

@ -1,20 +1,12 @@
import axios, { AxiosRequestConfig, AxiosInstance } from "axios";
const initialization = (config: AxiosRequestConfig): AxiosInstance => {
//axios.defaults.headers.common["X-Requested-With"] = "XMLHttpRequest";
delete axios.defaults.headers.common["X-Requested-With"];
// axios.defaults.withCredentials = true;
// const token = document.head.querySelector('meta[name="csrf-token"]');
// if (token) {
// axios.defaults.headers.common["X-CSRF-TOKEN"] = token.innerHTML;
// }
const axiosInstance = axios.create(config);
/*
Add default headers, interceptors etc..
*/
const axiosInstance = axios.create(config);
return axiosInstance;
};
export default initialization;
export default initialization;

View File

@ -1,31 +1,16 @@
import { AxiosRequestConfig } from "axios";
import { stringify } from "qs";
// let headers: AxiosRequestConfig['headers'] =
// headers['Content-Type'] = 'multipart/form-data';
/* This file configures Axios to send requests with the specified headers and parameters serialization format for URL-encoded form data */
export const axiosRequestConfiguration: AxiosRequestConfig = {
// responseType: "text",
// headers: {
// // "Content-Type": "text/plain",
// "Content-Type": "application/x-www-form-urlencoded",
// // "Content-Type": "application/x-www-form-urlencoded",
// // credentials: "same-origin",
// // "Access-Control-Allow-Credentials": "true",
// // "Access-Control-Allow-Origin": "*",
// },
headers: {
//: AxiosHeaders | Partial<RawAxiosHeaders & MethodsHeaders & CommonHeaders> | undefined
"Content-Type": "application/x-www-form-urlencoded",
},
// headers: {
// "Content-type": "application/json; charset=UTF-8",
// },
// paramsSerializer: {
// indexes: null, // by default: false
// },
paramsSerializer: {
/* The serialize function takes an object of key-value pairs as input and uses the qs.stringify method to convert it into a URL-encoded string.
The arrayFormat: "repeat" option specifies how arrays should be serialized in the URL parameters. */
serialize: (params: Record<string, number>) => {
return stringify(params, { arrayFormat: "repeat" });
},

View File

@ -20,6 +20,7 @@ export default class FacetCategory extends Vue {
filterName!: string;
get alias(): string {
// console.log("filterName:", this.filterName);
return this.filterName == "datatype" ? "doctype" : this.filterName;
}
@ -53,6 +54,7 @@ export default class FacetCategory extends Vue {
@Emit("filter")
activateItem(filterItem: FacetItem): FacetItem {
// console.log("ActivateItem");
filterItem.category = this.alias;
filterItem.active = true;
// this.$emit("filter", filterItem);

View File

@ -2,12 +2,17 @@
// import debounce from 'lodash/debounce';
// import { DatasetService } from "../../services/dataset.service";
import DatasetService from "../../services/dataset.service";
import { SolrSettings } from "@/models/solr";
// import { ref } from "vue";
// import { SolrSettings } from "@/models/solr"; // PENDING USE
import { OpenSettings } from "@/models/solr";
import { Component, Vue, Prop, Emit } from "vue-facing-decorator";
// import { Prop, Emit } from "vue-property-decorator";
import { Dataset, Suggestion, SearchType } from "@/models/dataset";
import { SOLR_HOST, SOLR_CORE } from "@/constants";
// import { SOLR_HOST, SOLR_CORE } from "@/constants";
import { OPEN_HOST, OPEN_CORE } from "@/constants"; // PENDING USE
import { HitHighlight } from "@/models/headers";
import DOMPurify from 'dompurify'; // To sanitize the HTML content to prevent XSS attacks!
@Component({
name: "VsInput",
@ -16,30 +21,40 @@ export default class VsInput extends Vue {
// @Prop()
// private title!: string;
// Define the placeholder text for the input field
@Prop({ default: "Search" })
readonly placeholder!: string;
private display = "";
private display = ""; // Input display value
@Prop()
private propDisplay = "";
private value!: Suggestion | string;
private error = "";
private results: Array<Dataset> = [];
private loading = false;
private selectedIndex = -1;
// private selectedDisplay = "";
private solr: SolrSettings = {
core: SOLR_CORE, //"rdr_data", // SOLR.core;
host: SOLR_HOST, //"tethys.at",
private results: Array<Dataset> = []; // Array to store search results
private highlights: Array<HitHighlight> = [];
private loading = false; // Loading state indicator
private selectedIndex = -1; // Index of the currently selected suggestion
// private solr: SolrSettings = {
// core: SOLR_CORE, //"rdr_data", // SOLR.core;
// host: SOLR_HOST, //"tethys.at",
// };
private openSearch: OpenSettings = {
core: OPEN_CORE, //"rdr_data", // SOLR.core;
host: OPEN_HOST, //"tethys.at",
// core: "test_data", // SOLR.core;
// host: "repository.geologie.ac.at",
};
// private rdrAPI!: DatasetService;
itemRefs!: Array<Element>;
emits = ["filter"];
// private rdrAPI!: DatasetService;
itemRefs!: Array<Element>; // Array to store references to suggestion items
emits = ["filter"]; // Emits filter event
// Set reference for each item
setItemRef(el: Element): void {
this.itemRefs.push(el);
}
@ -68,53 +83,113 @@ export default class VsInput extends Vue {
return this.error !== null;
}
// Computed property to generate suggestions based on search results
get suggestions(): Suggestion[] {
// const suggestion = {
// titles: new Array<string>(),
// authors: new Array<string>(),
// subjects: new Array<string>(),
// };
const suggestions = new Array<Suggestion>();
this.results.forEach((dataset) => {
// const del = dataset.title_output?.toLowerCase();
if (dataset.title_output.toLowerCase().includes(this.display.toLowerCase())) {
const title = dataset.title_output;
// if (!suggestion["titles"].find((value) => value === title)) {
// suggestion.titles.push(title);
// }
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 (this.find(dataset.author, this.display.toLowerCase()) !== "") {
const author = this.find(dataset.author, this.display.toLowerCase());
// console.log("getSuggestions > Display:", this.display);
// console.log("results:", this.results );
// console.log("highlights:", this.highlights);
//The method checks if there are any highlighted titles in the highlight object. If found, it joins the highlighted fragments into a single string
// Generate suggestions based on search results
this.results.forEach((dataset, index) => {
const hasAuthorSuggestion = suggestions.some((suggestion) => suggestion.value === author && suggestion.type == SearchType.Author);
const highlight = this.highlights[index];
// console.log("get suggestions:id", dataset.id);
// console.log("get suggestions:title_output", dataset.title_output);
// console.log("get suggestions:author", dataset.author);
// console.log("get suggestions:subjects", dataset.subjects);
// 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 highlightedTitle = 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.highlight.toLowerCase() === highlightedTitle.toLowerCase() && suggestion.type == SearchType.Title);
if (!hasTitleSuggestion) {
const suggestion = new Suggestion(dataset.title_output, highlightedTitle, SearchType.Title);
suggestions.push(suggestion);
}
}
if (highlight.author && highlight.author.length > 0) {
const highlightedAuthor = highlight.author.join(" ");
const datasetAuthor = this.find(dataset.author, this.display.toLowerCase());
const hasAuthorSuggestion = suggestions.some((suggestion) => suggestion.highlight.toLowerCase() === highlightedAuthor.toLowerCase() && suggestion.type == SearchType.Author);
if (!hasAuthorSuggestion) {
const suggestion = new Suggestion(author, SearchType.Author);
const suggestion = new Suggestion(datasetAuthor, highlightedAuthor, SearchType.Author);
suggestions.push(suggestion);
}
}
if (this.find(dataset.subject, this.display.toLowerCase()) != "") {
const subject = this.find(dataset.subject, this.display.toLowerCase());
const hasSubjectSuggestion = suggestions.some((suggestion) => suggestion.value === subject && suggestion.type == SearchType.Subject);
if (highlight.subjects && highlight.subjects.length > 0) {
const highlightedSubject = highlight.subjects.join(" ");
const datasetSubject = this.find(dataset.subjects, this.display.toLowerCase());
const hasSubjectSuggestion = suggestions.some((suggestion) => suggestion.highlight.toLowerCase() === highlightedSubject.toLowerCase() && suggestion.type == SearchType.Subject);
if (!hasSubjectSuggestion) {
const suggestion = new Suggestion(subject, SearchType.Subject);
const suggestion = new Suggestion(datasetSubject, highlightedSubject, SearchType.Subject);
suggestions.push(suggestion);
}
}
// ORIGINAL SOLR ===================================================================================================
// if (dataset.title_output.toLowerCase().includes(this.display.toLowerCase())) {
// const title = dataset.title_output;
// // Check if there is already a suggestion with this title and type
// const hasTitleSuggestion = suggestions.some((suggestion) => suggestion.value === title && suggestion.type == SearchType.Title);
// if (!hasTitleSuggestion) {
// // If there is no such suggestion, create a new one and add it to the suggestions array
// const suggestion = new Suggestion(title, SearchType.Title);
// suggestions.push(suggestion);
// }
// }
// if (this.find(dataset.author, this.display.toLowerCase()) !== "") {
// const author = this.find(dataset.author, this.display.toLowerCase());
// // Check if there is already a suggestion with this author and type
// 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 (this.find(dataset.subjects, this.display.toLowerCase()) != "") {
// const subject = this.find(dataset.subjects, this.display.toLowerCase());
// const hasSubjectSuggestion = suggestions.some((suggestion) => suggestion.value === subject && suggestion.type == SearchType.Subject);
// if (!hasSubjectSuggestion) {
// const suggestion = new Suggestion(subject, SearchType.Subject);
// suggestions.push(suggestion);
// }
// }
});
return suggestions;
}
/**
* This method combines the suggestion value and type into a single HTML string. It also sanitizes the HTML content using DOMPurify to prevent XSS attacks.
* The vue file uses the v-html directive to bind the combined HTML string to the label element. This ensures that the HTML content (e.g., <em>Wien</em>) is rendered correctly in the browser.
*/
formatSuggestion(result: Suggestion): string {
const sanitizedValue = DOMPurify.sanitize(result.highlight);
// Replacing the predefined format for highlights given by OpenSearch from <em> emphasys to <b> bold
const replacedValue = sanitizedValue.replace(/<em>/g, '<b>').replace(/<\/em>/g, '</b>');
return `${replacedValue} <em>| ${result.type}</em>`;
}
/**
* Clear all values, results and errors
**/
clear(): void {
console.log("clear");
this.display = "";
// this.value = null;
this.results = [];
@ -122,15 +197,20 @@ export default class VsInput extends Vue {
// this.$emit("clear");
}
/* When the search button is clicked or the search input is changed, it updates the value property of the component with the current value of display,
and emits a search-change event with the current value of display as the argument. */
@Emit("search-change")
search(): string {
console.log("search");
this.results = [];
// this.$emit("search", this.display)
this.value = this.display; //(obj["title_output"]) ? obj["title_output"] : obj.id
return this.display;
}
// Handler for search input change
searchChanged(): void {
// console.log("Search changed!");
this.selectedIndex = -1;
// Let's warn the parent that a change was made
// this.$emit("input", this.display);
@ -142,7 +222,9 @@ export default class VsInput extends Vue {
}
}
// Perform the search request
private resourceSearch() {
// console.log("resourceSearch");
if (!this.display) {
this.results = [];
return;
@ -152,23 +234,29 @@ export default class VsInput extends Vue {
this.request();
}
// Make the API request to search for datasets
private request(): void {
DatasetService.searchTerm(this.display, this.solr.core, this.solr.host).subscribe({
next: (res: Dataset[]) => this.dataHandler(res),
console.log("request()");
// DatasetService.searchTerm(this.display, this.solr.core, this.solr.host).subscribe({
DatasetService.searchTerm(this.display, this.openSearch.core, this.openSearch.host).subscribe({
// next: (res: Dataset[]) => this.dataHandler(res),
next: (res: { datasets: Dataset[], highlights: HitHighlight[] }) => this.dataHandler(res.datasets, res.highlights),
error: (error: string) => this.errorHandler(error),
complete: () => (this.loading = false),
});
}
private dataHandler(datasets: Dataset[]): void {
// Handle the search results
private dataHandler(datasets: Dataset[], highlights: HitHighlight[]): void {
this.results = datasets;
// this.$emit("search", this.display);
// this.loading = false;
this.highlights = highlights; // Store highlights
// console.log(datasets);
}
// Handle errors from the search request
private errorHandler(err: string): void {
this.error = err;
// this.loading = false;
}
/**
@ -180,7 +268,9 @@ export default class VsInput extends Vue {
return key === this.selectedIndex;
}
// Handle arrow down key press to navigate suggestions
onArrowDown(ev: Event): void {
console.log("onArrowDown");
ev.preventDefault();
if (this.selectedIndex === -1) {
this.selectedIndex = 0;
@ -190,6 +280,7 @@ export default class VsInput extends Vue {
this.fixScrolling();
}
// Scroll the selected suggestion into view
private fixScrolling() {
const currentElement = this.itemRefs[this.selectedIndex];
currentElement.scrollIntoView({
@ -199,7 +290,9 @@ export default class VsInput extends Vue {
});
}
// Handle arrow up key press to navigate suggestions
onArrowUp(ev: Event): void {
console.log("onArrowUp");
ev.preventDefault();
if (this.selectedIndex === -1) {
this.selectedIndex = this.suggestions.length - 1;
@ -209,7 +302,10 @@ export default class VsInput extends Vue {
this.fixScrolling();
}
// Handle enter key press to select a suggestion
onEnter(): void {
console.log("onEnter");
if (this.selectedIndex === -1) {
// this.$emit("nothingSelected", this.display);
this.display && this.search();
@ -222,18 +318,18 @@ export default class VsInput extends Vue {
@Emit("search-change")
private select(obj: Suggestion): Suggestion {
// if (!obj) {
// return;
// }
this.value = obj; //(obj["title_output"]) ? obj["title_output"] : obj.id
this.display = obj.value; // this.formatDisplay(obj)
// this.selectedDisplay = this.display;
console.log("select:");
this.value = obj;
console.log(obj);
this.display = obj.value;
this.close();
// this.$emit("update", this.value);
return this.value;
}
// Find a search term in an array
private find(myarray: Array<string>, searchterm: string): string {
for (let i = 0, len = myarray.length; i < len; i += 1) {
if (typeof myarray[i] === "string" && myarray[i].toLowerCase().indexOf(searchterm) !== -1) {
@ -248,15 +344,11 @@ export default class VsInput extends Vue {
* Close the results list. If nothing was selected clear the search
*/
close(): void {
console.log("close");
if (!this.value) {
this.clear();
}
// if (this.selectedDisplay !== this.display && this.value) {
// this.display = this.selectedDisplay;
// }
this.results = [];
this.error = "";
//this.removeEventListener()
// this.$emit("close");
}
}

View File

@ -1,10 +1,15 @@
<template>
<!-- Parent container with multiple rows -->
<div class="is-multiline">
<!-- <div class="content column is-half is-offset-one-quarter" style="margin-top: 30px; padding-bottom: 0; margin-bottom: 0px"> -->
<!-- Search input wrapper -->
<div class="column is-two-thirds-tablet is-half-desktop is-one-third-widescreen mx-auto">
<!-- Search box -->
<div class="search-box mx-auto">
<!-- Search field -->
<div class="field has-addons main-search-from-bg">
<div class="control is-expanded">
<!-- Input field for search query -->
<input
id="search_query"
v-model="display"
@ -20,24 +25,26 @@
@keydown.tab="close"
@focus="focus"
/>
<!-- <p>Display is: {{ display }}</p> -->
<!-- v-on:input="searchChanged" -->
</div>
<!-- Search button -->
<div class="control">
<button class="button input is-medium search-button-icon" @click="search()">
<!-- <img src="../../assets/fa/search.svg" style="height: 22px; width: 22px" /> -->
<!-- Search icon -->
<i class="fas fa-search text-white"></i>
</button>
</div>
</div>
</div>
</div>
<!-- <div class="column is-half is-offset-one-quarter"> -->
<!-- Suggestions list -->
<div class="column is-two-thirds-tablet is-half-desktop is-one-third-widescreen mx-auto">
<ul v-show="showResults" class="autocomplete-results pure-u-23-24">
<!-- Loading indicator -->
<li v-if="isLoading" class="loading">Loading results...</li>
<!-- Iterating over suggestions -->
<li
v-for="(result, i) in suggestions"
v-else
@ -47,8 +54,10 @@
v-bind:class="{ 'is-active': isSelected(i) }"
@click.prevent="select(result)"
>
<!-- Displaying suggestion result -->
<div class="small-label">
<label>{{ result.value }} ({{ result.type }})</label>
<!-- <label>{{ result.value }} ({{ result.type }})</label> -->
<label v-html="formatSuggestion(result)"></label>
</div>
</li>
</ul>
@ -140,7 +149,7 @@ input {
.autocomplete-result-item {
list-style: none;
text-align: left;
/* padding: 7px 10px; */
padding: 0px 0px 0px 5px; // top,right,bottom,left
cursor: pointer;
}

View File

@ -23,13 +23,15 @@ export default class VsResult extends Vue {
.join(".");
}
private convert(unixtimestamp: number): string {
// private convert(unixtimestamp: number): string { // SOLR
private convert(unixtimestamp: string): string { // OpenSearch
// Unixtimestamp
// var unixtimestamp = document.getElementById('timestamp').value;
// Months array
const months_arr = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
// Convert timestamp to milliseconds
const date = new Date(unixtimestamp * 1000);
// const date = new Date(unixtimestamp * 1000); // SOLR
const date = new Date(Number(unixtimestamp) * 1000); // OpenSearch
// Year
const year = date.getFullYear();
// Month

View File

@ -3,49 +3,25 @@
<div class="card result-list-container">
<div class="card-content record-elem">
<p v-if="document.identifier && document.identifier.length > 0">
<!-- <span>Author: {{ document.identifier.join(', ') }}</span> -->
<!-- <span v-for="(author,index) in document.author" :key="index">{{ author }}; </span> -->
<!-- <span>'https://doi.org/' + {{ document.identifier[0] }}</span> -->
<a target="_blank" v-bind:href="'https://doi.org/' + document.identifier[0]">
{{ "https://doi.org/" + document.identifier[0] + " &#10148;" }} </a
>&nbsp;
<span v-if="document.author && document.author.length > 0" class="disabled">{{ document.author[0] }}</span>
</p>
<!-- <span class="label label-info" data-container="div" data-title="Publication date">
{{ convert(document.server_date_published) }}
</span>
<span class="label label-default ng-binding">{{ document.doctype }}</span>
<span v-if="openAccessLicences.includes(document.licence)" class="label label-success titlecase">Open Access</span> -->
<h4>
<!-- <a
v-if="document.identifier && document.identifier.length > 0"
target="_self"
v-bind:href="'https://doi.' + getDomainWithoutSubdomain() + '/' + document.identifier[0]"
class="ng-binding"
>
{{ document.title_output }}
</a> -->
<!-- <a target="_self" v-bind:href="'dataset/' + document.id" class="ng-binding">
{{ document.title_output }}
</a> -->
<router-link class="ng-binding" v-bind:to="{ name: 'dataset', params: { datasetId: document.id } }">{{
document.title_output
}}</router-link>
</h4>
<!-- <p v-if="document.author && document.author.length > 0">
<span>Author: {{ document.author.join(', ') }}</span>
<span v-for="(author, index) in document.author" :key="index">{{ author }}; </span>
</p> -->
<p class="clamped clamped-2">
<span class="disabled" data-container="div" data-title="Publication date">
{{ convert(document.server_date_published) + ":&nbsp;" }}
</span>
<span class="text">
{{ document.abstract_output }}
<!-- {{ document.abstract_output }} -->
{{ document.abstract[0] }}
<span class="ellipsis">...</span>
<span class="fill"></span>
</span>
@ -53,11 +29,9 @@
<p>
<span class="label"><i class="fas fa-file"></i> {{ document.doctype }}</span>
<!-- <span>Licence: {{ document.licence }}</span> -->
<span v-if="openAccessLicences.includes(document.licence)" class="label titlecase"><i class="fas fa-lock-open"></i> Open Access</span>
</p>
<!-- <span class="label label-keyword titlecase" v-for="(item, index) in document.subject" :key="index"> #{{ item }} </span> -->
</div>
</div>
</div>

View File

@ -2,19 +2,31 @@
// declare const EDGE_URL: string;
declare const APP_URL: string;
declare const VUE_API: string;
declare const SOLR_HOST: string;
declare const SOLR_CORE: string;
// declare const SOLR_HOST: string;
// declare const SOLR_CORE: string;
// OPENSEARCH
declare const OPEN_HOST: string;
declare const OPEN_CORE: string;
// const _EDGE_URL = EDGE_URL;
// const _POINT_URL = POINT_URL;
const _APP_URL = APP_URL;
const _VUE_API = VUE_API;
const _SOLR_HOST = SOLR_HOST;
const _SOLR_CORE = SOLR_CORE;
// const _SOLR_HOST = SOLR_HOST;
// const _SOLR_CORE = SOLR_CORE;
// OPENSEARCH
const _OPEN_HOST = OPEN_HOST;
const _OPEN_CORE = OPEN_CORE;
// export { _EDGE_URL as EDGE_URL };
// export { _POINT_URL as POINT_URL };
export { _APP_URL as APP_URL };
export { _VUE_API as VUE_API };
export { _SOLR_HOST as SOLR_HOST };
export { _SOLR_CORE as SOLR_CORE };
// export { _SOLR_HOST as SOLR_HOST };
// export { _SOLR_CORE as SOLR_CORE };
// OPENSEARCH
export { _OPEN_HOST as OPEN_HOST };
export { _OPEN_CORE as OPEN_CORE };

View File

@ -1,72 +1,91 @@
// import moment from "moment";
import dayjs from "dayjs";
// // SOLR Dataset original
// export interface Dataset {
// abstract_additional: Array<string>;// OpenSearch: abstract: Array<string>
// abstract_output: string;// -----
// author: Array<string>;// EQUAL
// author_sort: Array<string>;// -----
// belongs_to_bibliography: boolean;// EQUAL
// creating_corporation: string;// EQUAL
// doctype: string;// EQUAL
// geo_location: string;// EQUAL
// id: number;// EQUAL
// identifier: Identifier;// OpenSearch: identifier: Array<string>
// language: string;// EQUAL
// licence: string;// EQUAL
// publisher_name: string;// EQUAL
// server_date_published: Array<number>;// OpenSearch not array!
// subject: Array<string>;// OpenSearch: subjectS
// title_output: string;// EQUAL
// year: number;// EQUAL
// year_inverted: number;// EQUAL
// }
// OpenSearch Dataset
export interface Dataset {
abstract_additional: Array<string>;
abstract_output: string;
author: Array<string>;
author_sort: Array<string>;
belongs_to_bibliography: boolean;
creating_corporation: string;
doctype: string;
geo_location: string;
id: number;
identifier: Identifier;
language: string;
licence: string;
publisher_name: string;
server_date_published: Array<number>;
subject: Array<string>;
title_output: string;
year: number;
year_inverted: number;
abstract: Array<string>;// OpenSearch: abstract: Array<string>
// abstract_output: string;// ----- NOT in OpenSearch
author: Array<string>;// EQUAL
// author_sort: Array<string>;// ----- NOT in OpenSearch
belongs_to_bibliography: boolean;// EQUAL
creating_corporation: string;// EQUAL
doctype: string;// EQUAL
geo_location: string;// EQUAL
id: number;// EQUAL
// identifier: Identifier;// OpenSearch: identifier: Array<string>
identifier: Array<string>// DIFF DATATYPE
language: string;// EQUAL
licence: string;// EQUAL
publisher_name: string;// EQUAL
// server_date_published: Array<number>;// OpenSearch string!
server_date_published: string;// DIFF DATATYPE
// subject: Array<string>;// OpenSearch: subjectS
subjects: Array<string>;// DIFF DATATYPE
title_output: string;// EQUAL
year: number;// EQUAL
year_inverted: number;// EQUAL
title: string // Unique in OpenSearch
title_additional: Array<string> // Unique in OpenSearch
bbox_xmin: string // Unique in OpenSearch
bbox_xmax: string // Unique in OpenSearch
bbox_ymin: string // Unique in OpenSearch
bbox_ymax: string // Unique in OpenSearch
reference: Array<string> // Unique in OpenSearch
abstract_additional: Array<string>;// Unique in OpenSearch
}
export class Suggestion {
constructor(
public value: string,
public type: SearchType,
public value: string, // Store the text value returned by OpenSearch
// Store the highlight: i.e. the text value with the emphasised term that generated that results by OpenSearch.
// In this way we can highlight the real term existing in the publication independently of how different was the inserted term used for the FUZZY search. e.g. "Vien" fuzzy matched with "Wien"
public highlight: string,
public type: SearchType, // Type of search element
) {}
// value!: string;
// type!: SearchType;
}
// export class Suggestion {
// constructor(
// public value: string,
// public type: SearchType,
// ) {}
// // value!: string;
// // type!: SearchType;
// }
export enum SearchType {
Title = "title",
Author = "author",
Subject = "subject",
Subject = "subjects" // ** !! The field has this name in OpenSearch!!
}
export class DbDataset {
// public id!: number;
// public url!: string;
// public contributing_corporation!: string;
// public creating_corporation!: string;
// public publisher_name!: string;
// public embargo_date!: string;
// public publish_id!: number;
// public project_id!: number;
// public type!: string;
// public language!: string;
// public server_state!: string;
// public belongs_to_bibliography!: boolean;
// public created_at!: string;
// public server_date_modified!: string;
// public server_date_published!: string;
// public account_id!: number;
// public editor_id!: number;
// public reviewer_id!: number;
// public preferred_reviewer!: number;
// public preferred_reviewer_email!: string;
// public reject_editor_note!: string;
// public reject_reviewer_note!: string;
// public reviewer_note_visible!: string;
// public titles!: Array<Title>;
// public abstracts!: Array<Abstract>;
// public authors!: Array<Author>;
// public contributors!: Array<Author>;
// public user!: Person;
// public subjects!: Array<Subject>;
constructor(
public id: string,

View File

@ -18,19 +18,6 @@ export interface ResponseHeaderParams {
rows?: number;
start?: number;
wt?: string;
// 0:'fl=id,licence,server_date_published,abstract_output,identifier,title_output,title_additional,author,subject,doctype'
// df:'title'
// facet:'on'
// indent:'on'
// json.facet.language:'{ type: "terms", field: "language" }'
// json.facet.subject:'{ type: "terms", field: "subject" }'
// q:'title:Geodaten - Blatt 49 Wels (1:50.000)'
// q.op:'and'
// rows:'10'
// start:'0'
// wt:'json'
}
export interface ResponseContent {
@ -39,36 +26,21 @@ export interface ResponseContent {
docs: Array<Dataset>;
}
// export interface FacetCount {
// facet_fields: FacetCategory<any>;
// }
// export class FacetCategory<T> {
// [key: string]: {
// values: T[];
// };
// }
//Used
export class FacetResults {
// language!: Array<FacetItem>;
// subject!: Array<FacetItem>;
[key: string]: Array<FacetItem>;
}
//#region solr response facets
export class FacetFields {
// count: number;
language!: FacetInstance;
subject!: FacetInstance;
// [key: string]: FacetInstance;
}
export interface FacetInstance {
[key: string]: Array<FacetItem>;
// buckets: Array<FacetItem>;
}
//Used
export class FacetItem {
val: string;
count: number;
@ -83,3 +55,77 @@ export class FacetItem {
}
}
//#endregion
// OPENSEARCH
// ========================================================================
export interface OpenSearchResponse {
took: number;
timed_out: boolean;
_shards: Shards;
hits: Hits; // Equivalent SOLR: response > docs
aggregations?: Aggregations; // Equivalent SOLR: facets
}
export interface Shards {
total: number;
successful: number;
skipped: number;
failed: number;
}
export interface Hits {
total: Total;
max_score: number;
hits: Array<Hit>;
}
export interface Total {
value: number; // Equivalent SOLR: response > numFound
relation: string;
}
export interface Hit {
_index: string;
_id: string;
_score: number;
_source: Dataset;
highlight: HitHighlight; // !! This name is to avoid collision with Typescript "Highlight" class
}
export interface HitHighlight {
subjects?: Array<string>;
title?: Array<string>;
author?: Array<string>;
}
export interface Aggregations { // Equivalent SOLR: FacetFields
subjects: Subjects;
language: Language;
}
export interface Subjects {
doc_count_error_upper_bound: number;
sum_other_doc_count: number;
buckets: Array<Bucket>;
}
export interface Language {
doc_count_error_upper_bound: number;
sum_other_doc_count: number;
buckets: Array<Bucket>;
}
export interface Bucket {
key: string;
doc_count: number;
}
// // Needed?
// export interface Aggregations {
// [key: string]: Aggregation;
// }
// export interface Aggregation {
// buckets: Array<Bucket>;
// }

View File

@ -1,4 +1,9 @@
export interface SolrSettings {
// export interface SolrSettings {
// core: string;
// host: string;
// }
export interface OpenSettings {
core: string;
host: string;
}

View File

@ -1,361 +1,620 @@
import api from "../api/api";
// import { Observable, of } from "rxjs";
import { Observable } from "rxjs";
import { map } from "rxjs/operators";
import { tap, map } from "rxjs/operators";
import { Dataset, DbDataset, Suggestion } from "@/models/dataset";
import { SolrResponse } from "@/models/headers";
import { HitHighlight, OpenSearchResponse, SolrResponse } from "@/models/headers";
import { ActiveFilterCategories } from "@/models/solr";
import { VUE_API } from "@/constants";
// import { deserialize, instanceToInstance } from "class-transformer";
import { deserialize } from "class-transformer";
// import { OAI_DATASETS } from "./mock-oai-datasets";
// import { OaiDataset, OaiPerson } from "@/models/oai";
// import xml2js from "xml2js";
class DatasetService {
// for the autocomplete search
public searchTerm(term: string, solrCore: string, solrHost: string): Observable<Dataset[]> {
// solr endpoint
// const host = 'http://voyagerdemo.com/';
// const host = 'https://www.tethys.at/';''
const host = "https://" + solrHost;
const path = "/solr/" + solrCore + "/select?";
// const base = "https://geomon.geologie.ac.at/52n-sos-webapp/api/features"; //host + path;
/**
* 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.
*/
/* 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[] }> {
// OpenSearch endpoint
const host = openHost; // When using local OpenSearch dev endpoint
const path = "/" + openCore + "/_search";
const base = host + path;
//const fields = 'id,server_date_published,abstract_output,title_output,title_additional,author,subject'; // fields we want returned
const fields = [
"id",
"licence",
"server_date_published",
"abstract_output",
"title_output",
"title_additional",
"author",
"subject",
"doctype",
].toString();
//var dismaxFields = "title^3 abstract^2 subject^1";
const qfFields = "title^3 author^2 subject^1";
// let params = "fl=" + fields;
// // if (term == "*%3A*") { // *:
// // params += "&defType=edismax&wt=json&indent=on"; //edismax
// // } else {
// params += "&defType=edismax&qf=" + qfFields + "&wt=json&indent=on"; //dismax
// // }
// const query = "&q=" + term + "*";
// const apiU = base + params + query;
const q_params = {
"0": "fl=" + fields,
q: term + "*",
defType: "edismax",
qf: qfFields,
indent: "on",
wt: "json",
/**
* 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 = {
query: {
bool: {
should: [
{ match: { title: { query: term, fuzziness: "AUTO", boost: 3 } } },
{ match: { author: { query: term, fuzziness: "AUTO", boost: 2 } } },
{ match: { subjects: { query: term, fuzziness: "AUTO", boost: 1 } } }, // In SOLR is "subject"!
{ wildcard: { title: { value: `${lowercaseTerm}*`, boost: 3 } } },
{ wildcard: { author: { value: `${lowercaseTerm}*`, boost: 2 } } },
{ wildcard: { subjects: { value: `${lowercaseTerm}*`, boost: 1 } } } // In SOLR is "subject"!
],
minimum_should_match: 1
}
},
size: 10,
from: 0,
// 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"!
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
},
// // 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: {}
}
}
};
const stations = api.get<SolrResponse>(base, q_params).pipe(map((res: SolrResponse) => res.response.docs));
return stations;
// Make API call to OpenSearch and return the result
/**
* When a POST request is made to the OpenSearch server using the api.post<OpenSearchResponse> 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<OpenSearchResponse>(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)
}))
);
}
public facetedSearch(
// // For the autocomplete search. Method to perform a search based on a term
// public searchTerm_SOLR(term: string, solrCore: string, solrHost: string): Observable<Dataset[]> {
// // SOLR endpoint
// const host = "https://" + solrHost;
// const path = "/solr/" + solrCore + "/select?";
// const base = host + path;
// //const fields = 'id,server_date_published,abstract_output,title_output,title_additional,author,subject'; // fields we want returned
// const fields = [
// "id",
// "licence",
// "server_date_published",
// "abstract_output",
// "title_output",
// "title_additional",
// "author",
// "subject",
// "doctype",
// ].toString();
// const qfFields = "title^3 author^2 subject^1";
// const q_params = {
// "0": "fl=" + fields,
// q: term + "*",
// defType: "edismax",
// qf: qfFields,
// indent: "on",
// wt: "json",
// };
// // Make API call to Solr and return the result
// /**
// * When a GET request is made to the Solr server using the api.get<SolrResponse> method, the response received from Solr is an object that includes various details about the search results.
// * One of the key properties of this response object is docs, 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.
// */
// const stations = api.get<SolrResponse>(base, q_params).pipe(map((res: SolrResponse) => res.response.docs));
// return stations;
// }
// public facetedSearchOPEN(
// suggestion: Suggestion | string,
// activeFilterCategories: ActiveFilterCategories,
// openCore: string,
// openHost: string,
// start?: string, // Starting page
// ): Observable<OpenSearchResponse> {
// // 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<OpenSearchResponse>(base, body);
// return stations;
// }
public facetedSearchOPEN(
suggestion: Suggestion | string,
activeFilterCategories: ActiveFilterCategories,
solrCore: string,
solrHost: string,
start?: string,
): Observable<SolrResponse> {
// solr endpoint
// const host = 'http://voyagerdemo.com/';
//const host = 'https://www.tethys.at/';
//const host = 'https://' + solrHost;
const host = "https://" + solrHost;
const path = "/solr/" + solrCore + "/select?";
openCore: string,
openHost: string,
start?: string, // Starting page
): Observable<OpenSearchResponse> {
// 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. */
// When suggestion is a string:
const mainQuery = typeof suggestion === 'string'
? {
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
}
}
// When suggestion is a suggestion object
: {
match: {
[suggestion.type]: {
query: suggestion.value,
operator: 'and' // all the terms in the query must be present in the field
}
}
};
// CONTABO ====================================================
// // Constructing Filters Based on Active Filter Categories
// const filters = Object.entries(activeFilterCategories).map(([category, values]) => ({
// terms: { [`${category}.keyword`]: values }
// }));
// ================================================================
//const fields = 'id,server_date_published,abstract_output,title_output,title_additional,author,subject'; // fields we want returned
const fields = [
"id",
"licence",
"server_date_published",
"abstract_output",
"identifier",
"title_output",
"title_additional",
"author",
"subject",
"doctype",
].toString();
// // 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 qfFields = "title^3 author^2 subject^1";
// let params = "fl=" + fields;
// if (term == "*%3A*") {
// params += "&defType=edismax&wt=json&indent=on"; //edismax
// } else {
// params += "&defType=dismax&qf=" + qfFields + "&wt=json&indent=on"; //dismax
// }
let term, queryOperator, qfFields;
if (typeof suggestion === "string") {
term = suggestion + "*";
queryOperator = "or";
qfFields = "title^3 author^2 subject^1";
} else if (suggestion instanceof Suggestion) {
term = suggestion.type + ':"' + suggestion.value + '"';
queryOperator = "and";
qfFields = undefined;
}
// const filters = Object.entries(activeFilterCategories).map(([category, values]) =>
// values.map(value => ({ term: { [`${category}.keyword`]: value } }))
// ).flat();
if (start === undefined) start = "0";
// start = "&start=" + start;
// const facetFields =
// "&facet=on&facet.field=language&facet.field={!key=datatype}doctype&facet.field=subject"; //&fq=year:(2019)";//&facet.query=year:2018";
const filterFields = new Array<string>();
if (Object.keys(activeFilterCategories).length > 0) {
// filterFields = '["';
// const filterFields: string[] = [];
// const facet_fields: FacetFields = res.facets;
let prop: keyof typeof activeFilterCategories;
for (prop in activeFilterCategories) {
const filterItems = activeFilterCategories[prop];
// const filterItems = facetCategory.values;
// filterItems.forEach((filterItem) => {
// console.log(`${key} ${valueArray}`);
filterItems.forEach(function (value: string) {
// filterFields += "&fq=" + key + ':("' + value + '")';
filterFields.push(prop + ':("' + value + '")');
// filterFields += prop + ":" + value;
});
const filters = Object.entries(activeFilterCategories).map(([category, values]) => {
if (category === "language" || category === "year") {
return values.map(value => ({ term: { [category]: value } }));
} else {
return values.map(value => ({ term: { [`${category}.keyword`]: value } }));
}
// filterFields += '"]';
}
// const query = "&sort=server_date_published desc" + "&q=" + term;
}).flat();
// const api =
// base + params + limit + start + query + filterFields + facetFields;
// console.log(activeFilterCategories);
console.log("mainQuery:", mainQuery);
console.log("filters:", filters);
// https://solr.apache.org/guide/8_4/json-request-api.html
const q_params = {
"0": "fl=" + fields,
q: term,
"q.op": queryOperator,
defType: "edismax",
qf: qfFields,
// df: "title",
indent: "on",
wt: "json",
rows: 10,
// fq: ["subject:Steiermark", "language:de"],
fq: filterFields,
start: start,
sort: "server_date_published desc",
facet: "on",
// "facet.field": "language",
"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 }',
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: [
mainQuery, // Ensure the main query must be satisfied
...filters // Ensure all filters must be satisfied
]
}
},
// // 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: [{ _score: { order: "desc" } }],
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.
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
},
// 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: {}
}
}
};
console.log("body:", body);
const stations = api.get<SolrResponse>(base, q_params);
// .pipe(map((res) => res.response.docs));
const stations = api.post<OpenSearchResponse>(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(
// suggestion: Suggestion | string,
// activeFilterCategories: ActiveFilterCategories,
// solrCore: string,
// solrHost: string,
// start?: string, // Starting page
// ): Observable<SolrResponse> {
// // console.log("face:", suggestion);
// // console.log(activeFilterCategories);
// // console.log(solrCore);
// // console.log(solrHost);
// // console.log(start);
// console.log("facetedsearchSOLR > suggestion entered:");
// console.log(suggestion);
// // Construct Solr query parameters
// const host = "https://" + solrHost;
// const path = "/solr/" + solrCore + "/select?";
// const base = host + path;
// const fields = [
// "id",
// "licence",
// "server_date_published",
// "abstract_output",
// "identifier",
// "title_output",
// "title_additional",
// "author",
// "subject",
// "doctype",
// ].toString();
// // Determine search term, query operator, and query fields based on the suggestion type. Depending on whether suggestion is a string or a Suggestion object, it constructs the search term and query fields differently.
// let term, queryOperator, qfFields;
// if (typeof suggestion === "string") { // f suggestion is a string, it appends a wildcard (*) for partial matches.
// term = suggestion + "*";
// queryOperator = "or";
// qfFields = "title^3 author^2 subject^1";
// } else if (suggestion instanceof Suggestion) { // If suggestion is a Suggestion object, it forms a more specific query based on the type and value of the suggestion.
// term = suggestion.type + ':"' + suggestion.value + '"';
// queryOperator = "and";
// qfFields = undefined;
// }
// // Set default value for start if not provided
// if (start === undefined) start = "0";
// // Construct filter fields based on active filter categories
// const filterFields = new Array<string>();
// 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
// that can exist on the activeFilterCategories --> prop can only be assigned a value that is a key of the activeFilterCategories object */
// let prop: keyof typeof activeFilterCategories;
// for (prop in activeFilterCategories) {
// const filterItems = activeFilterCategories[prop];
// filterItems.forEach(function (value: string) {
// filterFields.push(prop + ':("' + value + '")');
// // e.g. Array [ 'subject:("Vektordaten")', 'author:("GeoSphere Austria, ")' ]
// });
// }
// }
// // https://solr.apache.org/guide/8_4/json-request-api.html
// // Construct Solr query parameters
// const q_params = {
// "0": "fl=" + fields,
// q: term,
// "q.op": queryOperator,
// defType: "edismax",
// qf: qfFields,
// // df: "title",
// indent: "on",
// wt: "json",
// rows: 10,
// // fq: ["subject:Steiermark", "language:de"],
// fq: filterFields,
// start: start,
// sort: "server_date_published desc",
// facet: "on",
// // "facet.field": "language",
// "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 }',
// };
// /* 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);
// return stations;
// }
// Method to fetch years
public getYears(): Observable<string[]> {
// const heroes = of(HEROES);
// const host = "https:" + VUE_API;
const host = VUE_API;
const path = "/api/years";
const base = host + path;
const years = api.get<string[]>(base);
// this.messageService.add('HeroService: fetched heroes');
return years;
}
// Method to fetch documents for a specific year
public getDocuments(year: string): Observable<Array<DbDataset>> {
// const host = "https:" + VUE_API;
const host = VUE_API;
const path = "/api/sitelinks/" + year;
const base = host + path;
const documents: Observable<DbDataset[]> = api.get<Array<DbDataset>>(base);
// this.messageService.add('HeroService: fetched heroes');
return documents;
}
// Method to fetch a dataset by its ID
public getDataset(id: number): Observable<DbDataset> {
// const host = "https:" + VUE_API;
const host = VUE_API;
const path = "/api/dataset/" + id;
const apiUrl = host + path;
const dataset = api.get<DbDataset>(apiUrl).pipe(map((res) => this.prepareDataset(res)));
// const dataset = api.get<DbDataset>(apiUrl).pipe(map((res) => this.prepareDataset(res, apiUrl)));
// this.messageService.add('HeroService: fetched heroes');
return dataset;
}
// Method to fetch a dataset by its DOI
public getDatasetByDoi(doi: string): Observable<DbDataset> {
// const host = "https:" + VUE_API;
const host = VUE_API;
const path = "/api/dataset/10.24341/tethys." + doi;
const apiUrl = host + path;
const dataset = api.get<DbDataset>(apiUrl).pipe(map((res) => this.prepareDataset(res)));
// const dataset = api.get<DbDataset>(apiUrl).pipe(map((res) => this.prepareDataset(res, apiUrl)));
// this.messageService.add('HeroService: fetched heroes');
return dataset;
}
// public getOaiDatasets(): Observable<OaiDataset[]> {
// const apiUrl = "https://data.tethys.at/oai?verb=ListRecords&metadataPrefix=oai_datacite";
// const oaiDatasets = api.get<string>(apiUrl).pipe(
// map(
// (response: string) => {
// // const parser = new DOMParser();
// // const xmlDoc: XMLDocument = parser.parseFromString(response, "application/xml");
// // const xslDoc = parser.parseFromString(this.xsl, "application/xml");
// // const xsltProcessor = new XSLTProcessor();
// // xsltProcessor.importStylesheet(xslDoc);
// // console.log(xmlDoc);
// // const xmlDom = xsltProcessor.transformToDocument(xmlDoc);
// // const serializer = new XMLSerializer();
// // const html = serializer.serializeToString(xmlDom.documentElement);
// // console.log(html);
// // const arrOai = new Array<OaiDataset>();
// // return arrOai;
// const arrOai = this.parseXML(response);
// return arrOai;
// // .then((data) => {
// // return data;
// // });
// },
// // (error: string) => this.errorHandler(error),
// ),
// );
// // const oaiDatasets = of(OAI_DATASETS);
// // this.messageService.add('HeroService: fetched heroes');
// return oaiDatasets;
// }
// private parseXML(xmlStr: string): Array<OaiDataset> {
// // let k = "";
// const arr: OaiDataset[] = [];
// const domParser = new DOMParser();
// const doc = domParser.parseFromString(xmlStr, "application/xml");
// const records = doc.getElementsByTagName("ListRecords")[0];
// // // const rt = xmlNode.resumptionToken;
// // for (let i = 0; i < records.length; i++) {
// // console.log(records[i].getAttribute("name"));
// // }
// const parser: xml2js.Parser = new xml2js.Parser({
// trim: true,
// explicitArray: false,
// ignoreAttrs: false,
// // mergeAttrs: true,
// });
// parser.parseString(records.outerHTML, function (err: Error | null, result: any) {
// const xmlNode = result.ListRecords;
// // const rt = xmlNode.resumptionToken;
// for (const rNode in xmlNode.record) {
// const item = xmlNode.record[rNode];
// const dc = item.metadata.resource;
// const t = dc.titles.title;
// const id = dc.identifier._;
// const lang = "en"; //dc.titles.title.attributes("xml",True)->lang;
// let title: string;
// if (lang == "en" && t.length > 1) {
// title = t[1]._;
// } else {
// title = t[0]._;
// }
// let creator = "";
// if (dc.creators.creator instanceof Array) {
// dc.creators.creator.forEach((person: OaiPerson) => {
// creator += person.creatorName + "; ";
// });
// } else {
// creator += dc.creators.creator.creatorName._;
// }
// let contributor = "";
// if (dc.contributors) {
// if (dc.contributors.contributor instanceof Array) {
// dc.contributors.contributor.forEach((person: OaiPerson) => {
// contributor += person.contributorName + "; ";
// });
// } else {
// contributor += dc.contributors.contributor.contributorName;
// }
// }
// // ?.map((u: any) => u.creatorName._).join("; ");
// // foreach ($dc->creators->creator as $c) {
// // foreach ($c->creatorName as $d) {
// // if (count(explode(',',$d)) > 1) {
// // $creator .= explode(',',$d)[0] . ', ' . substr(explode(',',$d)[1],1,1) . '; ';
// // } else {
// // $creator .= explode(',',$d)[0];
// // }
// // }
// // }
// const north = dc.geoLocations.geoLocation.geoLocationBox.northBoundLatitude;
// const east = dc.geoLocations.geoLocation.geoLocationBox.eastBoundLongitude;
// const south = dc.geoLocations.geoLocation.geoLocationBox.southBoundLatitude;
// const west = dc.geoLocations.geoLocation.geoLocationBox.westBoundLongitude;
// const subject = dc.subjects.subject.map((u: any) => u._).join(", ");
// const oaiDataset = {
// doi: id,
// title: title,
// creator: creator,
// contributor: contributor,
// subject: subject,
// north: north,
// south: south,
// east: east,
// west: west,
// } as OaiDataset;
// arr.push(oaiDataset);
// }
// // resolve(arr);
// });
// return arr;
// }
// private prepareOAI(xml: any) : Array<OaiDataset> {
// //
// }
// private prepareDataset(datasetObj: DbDataset, apiUrl: string): DbDataset {
// Method to prepare dataset object
private prepareDataset(datasetObj: DbDataset): DbDataset {
const dataset = deserialize<DbDataset>(DbDataset, JSON.stringify(datasetObj));
dataset.url = document.documentURI;
// this.internalDatasetId.generateInternalId(dataset);
// if (dataset.seriesParameters) {
// dataset.parameters = dataset.seriesParameters;
// delete dataset.seriesParameters;
// }
return dataset;
}
}

View File

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

View File

@ -11,6 +11,9 @@ import { Suggestion } from "@/models/dataset";
export default class HomeViewComponent extends Vue {
public display = "";
/* This method is called when a search suggestion is selected. It takes a parameter suggestion which can be either a Suggestion object or a string.
If it's a string, the method extracts the term and navigates to the "Search" route with the term as a parameter. If it's a Suggestion object, it extracts
the value and type from the suggestion and navigates to the "Search" route with both parameters.*/
onSearch(suggestion: Suggestion | string): void {
let term;
if (typeof suggestion === "string") {
@ -22,6 +25,7 @@ export default class HomeViewComponent extends Vue {
}
}
/* This method is called when the user initiates a search. It navigates to the "Search" route with the display property as a parameter. */
search(): void {
this.$router.push({ name: "Search", params: { display: this.display } });
}

View File

@ -18,13 +18,6 @@
</a>
</div> -->
<!-- <div class="column">
<div class="col text-center py-3">
<h1>Tethys Research Data Repository</h1>
<p class="lead">Data Publisher for Geoscience Austria</p>
<hr class="center-line" />
</div>
</div> -->
<div class="column">
<div class="col text-center py-3">
<h1>Tethys Research Data Repository</h1>

View File

@ -3,16 +3,23 @@ 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 { 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 } 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 { ActiveFilterCategories } from "@/models/solr";
import { SOLR_HOST, SOLR_CORE } from "@/constants";
// import { SOLR_HOST, SOLR_CORE } from "@/constants";
import { IPagination } from "@/models/pagination";
import PaginationComponent from "@/components/PaginationComponent.vue";
import { OPEN_HOST, OPEN_CORE } from "@/constants";
// Decorate the component and define its name and components
@Component({
name: "SearchViewComponent",
components: {
@ -23,6 +30,8 @@ import PaginationComponent from "@/components/PaginationComponent.vue";
PaginationComponent,
},
})
// Define the SearchViewComponent class
export default class SearchViewComponent extends Vue {
@Prop()
display!: string;
@ -46,16 +55,22 @@ export default class SearchViewComponent extends Vue {
};
loaded = false;
numFound!: number;
private solr: SolrSettings = {
core: SOLR_CORE, //"rdr_data", // SOLR.core;
host: SOLR_HOST, //"tethys.at",
// core: "test_data", // SOLR.core;
// host: "repository.geologie.ac.at",
// private solr: SolrSettings = {
// core: SOLR_CORE, //"rdr_data", // SOLR.core;
// host: SOLR_HOST, //"tethys.at",
// };
private open: OpenSettings = {
core: OPEN_CORE, //"rdr_data", // SOLR.core;
host: OPEN_HOST, //"tethys.at",
};
// private rdrAPI!: DatasetService;
private error = "";
// Computed property to get search term as string
get stringSearchTerm(): string {
// console.log("stringSearchTerm:", this.searchTerm);
if (typeof this.searchTerm === "string") {
return this.searchTerm;
} else if (this.searchTerm instanceof Suggestion) {
@ -65,6 +80,7 @@ export default class SearchViewComponent extends Vue {
}
}
// Method to check if a search term is present
hasSearchTerm(): boolean {
if (typeof this.searchTerm === "string" && this.searchTerm !== "") {
return true;
@ -77,18 +93,25 @@ export default class SearchViewComponent extends Vue {
// getKeyName(value: string) {
// 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 {
const keys = Object.keys(myEnum).filter((x) => myEnum[x] == enumValue);
return keys.length > 0 ? keys[0] : null;
// return keys[0];
}
// Lifecycle hook: executed before the component is mounted
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" | null = this.getEnumKeyByEnumValue(SearchType, this.type);
if (enumKey) {
const suggestion = new Suggestion(this.display, SearchType[enumKey]);
const suggestion = new Suggestion(this.display, "NO-IDEA", SearchType[enumKey]);
// const suggestion = new Suggestion(this.display, "" , SearchType[enumKey]);
this.onSearch(suggestion);
} else {
this.onSearch(this.display);
@ -100,209 +123,327 @@ export default class SearchViewComponent extends Vue {
}
}
// onSearch(term: string): void {
// Method to trigger a search
onSearch(suggestion: Suggestion | string): void {
// let queryOperator;
// if (typeof suggestion === "string") {
// suggestion = suggestion + "*";
// queryOperator = "or";
// } else if (suggestion instanceof Suggestion) {
// // term = suggestion.value;
// queryOperator = "and";
// }
// if (term) {
// term = term.trim();
// } else {
// term = "*%3A*";
// }
// console.log("ONSEARCH");
// Reset active filter categories and facet results
this.activeFilterCategories = new ActiveFilterCategories();
this.facets = new FacetResults();
// this.facets = {};
this.searchTerm = suggestion;
DatasetService.facetedSearch(suggestion, this.activeFilterCategories, this.solr.core, this.solr.host, undefined).subscribe({
next: (res: SolrResponse) => this.dataHandler(res),
// 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({
// 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),
error: (error: string) => this.errorHandler(error),
});
}
private dataHandler(res: SolrResponse, filterItem?: FacetItem): void {
// this.results = datasets;
this.results = res.response.docs;
this.numFound = res.response.numFound;
// Handle the search results
private dataHandlerOPEN(res: OpenSearchResponse, filterItem?: FacetItem): void {
this.results = res.hits.hits.map(hit => hit._source);
this.numFound = res.hits.total.value;
// pagination
this.pagination["total"] = res.response.numFound;
this.pagination["perPage"] = res.responseHeader.params.rows as number;
// this.pagination["currentPage"] = 1;
this.pagination["data"] = res.response.docs;
this.pagination.lastPage = Math.ceil(this.pagination.total / this.pagination.perPage);
// facets
// const facet_fields = res.facet_counts.facet_fields;
// for (const prop in facet_fields) {
// const facetCategory: FacetCategory<any> = facet_fields[prop];
// const facetValues = facetCategory.key.values.map((facet_value: any, i: number) => {
// if (i % 2 === 0 && typeof facet_value == "string") {
// //var rObj = { value: facet, count: facet_fields[prop][i + 1] };
// // FiletrItem with value and count
// const rObj = new FilterItem(facet_value, facetCategory.key.values[i + 1]);
// return rObj;
// }
// });
// .filter(function (el: FilterItem) {
// return el != null && el.count > 0;
// });
// //this.facets.push({ filterName: prop, values: facetValues });
// this.facets[prop] = facetValues;
// 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);
// }
// }
const facet_fields: FacetFields = res.facets;
let prop: keyof typeof facet_fields;
for (prop in facet_fields) {
const facetCategory = facet_fields[prop];
if (facetCategory.buckets) {
const facetItems: Array<FacetItem> = facetCategory.buckets;
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);
let facetValues = facetItems.map((facetItem) => {
let rObj: FacetItem;
if (filterItem?.val == facetItem.val) {
rObj = filterItem;
} else if (this.facets[prop]?.some((e) => e.val === facetItem.val)) {
// console.log(facetValue + " is included")
const indexOfFacetValue = this.facets[prop].findIndex((i) => i.val === facetItem.val);
// console.log(indexOfFacetValue);
rObj = this.facets[prop][indexOfFacetValue];
rObj.count = facetItem.count;
// rObj = new FacetItem(val, count);
} else {
rObj = new FacetItem(facetItem.val, facetItem.count);
}
return rObj;
});
// if (res.aggregations) {
// const facet_fields = res.aggregations;
facetValues = facetValues.filter(function (el) {
return el != null && el.count > 0;
});
// this.facets[prop] = facetCategory;
this.facets[prop] = facetValues;
// 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;
let prop: keyof typeof facet_fields;
// Iterate through 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));
let facetValues = facetItems.map((facetItem) => {
let rObj: FacetItem;
// Check if current facet item matches filter item
if (filterItem?.val == facetItem.val) {
rObj = filterItem;
} else if (this.facets[prop]?.some((e) => e.val === facetItem.val)) {
const indexOfFacetValue = this.facets[prop].findIndex((i) => i.val === facetItem.val);
rObj = this.facets[prop][indexOfFacetValue];
rObj.count = facetItem.count;
} else {
// Create new facet item
rObj = new FacetItem(facetItem.val, facetItem.count);
}
return rObj;
});
// Filter out null values and values with count <= 0
facetValues = facetValues.filter(el => el.count > 0);
this.facets[prop] = facetValues;
}
}
}
}
// // Method to handle search response
// private dataHandler(res: SolrResponse, filterItem?: FacetItem): void {
// // console.log("dataHandlerSOLR (docs, numFound):");
// // console.log(res.response.docs);
// // console.log(res.response.numFound);
// // Update results
// this.results = res.response.docs;
// this.numFound = res.response.numFound;
// // Update pagination
// this.pagination["total"] = res.response.numFound;
// this.pagination["perPage"] = res.responseHeader.params.rows as number;
// this.pagination["data"] = res.response.docs;
// this.pagination.lastPage = Math.ceil(this.pagination.total / this.pagination.perPage);
// 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;
// // Iterate through facet fields
// for (prop in facet_fields) {
// const facetCategory = facet_fields[prop];
// if (facetCategory.buckets) {
// const facetItems: Array<FacetItem> = facetCategory.buckets;
// let facetValues = facetItems.map((facetItem) => {
// let rObj: FacetItem;
// // Check if current facet item matches filter item
// if (filterItem?.val == facetItem.val) {
// rObj = filterItem;
// } else if (this.facets[prop]?.some((e) => e.val === facetItem.val)) {
// // console.log(facetValue + " is included")
// // Update existing facet item with new count
// const indexOfFacetValue = this.facets[prop].findIndex((i) => i.val === facetItem.val);
// // console.log(indexOfFacetValue);
// rObj = this.facets[prop][indexOfFacetValue];
// rObj.count = facetItem.count;
// // rObj = new FacetItem(val, count);
// } else {
// // Create new facet item
// rObj = new FacetItem(facetItem.val, facetItem.count);
// }
// return rObj;
// });
// // Filter out null values and values with count <= 0
// facetValues = facetValues.filter(function (el) {
// return el != null && el.count > 0;
// });
// // this.facets[prop] = facetCategory;
// // Update facet values
// this.facets[prop] = facetValues;
// }
// }
// }
// Method to handle search errors
private errorHandler(err: string): void {
this.error = err;
// this.loading = false;
}
// Method to handle pagination
onMenuClick(page: number) {
// const test = page;
// console.log(test);
console.log("onMenuClick");
this.pagination.currentPage = page;
const start = page * this.pagination.perPage - this.pagination.perPage;
DatasetService.facetedSearch(this.searchTerm, this.activeFilterCategories, this.solr.core, this.solr.host, start.toString()).subscribe(
(res: SolrResponse) => this.dataHandler(res),
(error: string) => this.errorHandler(error),
);
// // Trigger new search with updated pagination parameters
// DatasetService.facetedSearch(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),
error: (error: string) => this.errorHandler(error),
});
}
// Method to handle facet filtering
onFilter(facetItem: FacetItem): void {
console.log("onFilter");
// 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<string>();
// 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);
DatasetService.facetedSearch(this.searchTerm, this.activeFilterCategories, this.solr.core, this.solr.host, undefined).subscribe(
(res: SolrResponse) => this.dataHandler(res, facetItem),
(error: string) => this.errorHandler(error),
);
// alert(this.activeFilterCategories[filter.Category]);
// var res = await rdrApi.search(this.searchTerm, this.activeFilterCategories, this.solrCore, this.solrHost);
// this.results = res.response.docs;
// this.numFound = res.response.numFound;
// // pagination
// this.pagination['total'] = res.response.numFound;
// this.pagination['per_page'] = res.responseHeader.params.rows;
// this.pagination['current_page'] = 1;
// this.pagination['data'] = res.response.docs;
// var facet_fields = res.facet_counts.facet_fields;
// for (var prop in facet_fields) {
// var facetValues = facet_fields[prop].map((facetValue, i) => {
// if (i % 2 === 0) {
// // var rObj = { value: facetValue, count: facet_fields[prop][i + 1] };
// var rObj;
// if (filter.value == facetValue) {
// rObj = filter;
// } else if (this.facets[prop].some(e => e.value === facetValue)) {
// // console.log(facetValue + " is included")
// var indexOfFacetValue = this.facets[prop].findIndex(i => i.value === facetValue);
// // console.log(indexOfFacetValue);
// rObj = this.facets[prop][indexOfFacetValue];
// rObj.count = facet_fields[prop][i + 1];
// } else {
// rObj = new FilterItem(facetValue, facet_fields[prop][i + 1]);
// }
// return rObj;
// }
// }).filter(function (el) {
// return el != null && el.count > 0;
// });
// // this.facets.push({ filterName: prop, values: facetValues });
// this.facets[prop] = facetValues;
// Trigger new search with updated filter
// DatasetService.facetedSearch(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),
error: (error: string) => this.errorHandler(error),
});
}
}
onClearFacetCategory(categoryName: string): void {
// alert(categoryName);
// // // Method to clear facet category filter
// onClearFacetCategory(categoryName: string): void {
// console.log("onClearFacetCategory");
// delete this.activeFilterCategories[categoryName];
// // Trigger new search with updated filter
// DatasetService.facetedSearch(this.searchTerm, this.activeFilterCategories, this.solr.core, this.solr.host, undefined).subscribe({
// next: (res: SolrResponse) => {
// this.results = res.response.docs;
// this.numFound = res.response.numFound;
// // pagination
// this.pagination["total"] = res.response.numFound;
// this.pagination["perPage"] = res.responseHeader.params.rows as number;
// this.pagination["currentPage"] = 1;
// this.pagination["data"] = res.response.docs;
// const facet_fields: FacetFields = res.facets;
// let prop: keyof typeof facet_fields;
// for (prop in facet_fields) {
// const facetCategory: FacetInstance = facet_fields[prop];
// if (facetCategory.buckets) {
// const facetItems: Array<FacetItem> = facetCategory.buckets;
// const facetValues = facetItems.map((facetItem) => {
// let rObj: FacetItem;
// if (this.facets[prop]?.some((e) => e.val === facetItem.val)) {
// // console.log(facetValue + " is included")
// // Update existing facet item with new count
// const indexOfFacetValue = this.facets[prop].findIndex((i) => i.val === facetItem.val);
// // console.log(indexOfFacetValue);
// rObj = this.facets[prop][indexOfFacetValue];
// rObj.count = facetItem.count;
// // rObj = new FacetItem(val, count);
// // if facet ccategory is reactivated category, deactivate all filter items
// if (prop == categoryName) {
// rObj.active = false;
// }
// } else {
// // Create new facet item
// rObj = new FacetItem(facetItem.val, facetItem.count);
// }
// return rObj;
// });
// this.facets[prop] = facetValues;
// }
// }
// },
// error: (error: string) => this.errorHandler(error),
// complete: () => console.log("clear facet category completed"),
// });
// }
// Method to clear facet category filter
onClearFacetCategoryOPEN(categoryName: string): void {
// console.log("onClearFacetCategory");
delete this.activeFilterCategories[categoryName];
DatasetService.facetedSearch(this.searchTerm, this.activeFilterCategories, this.solr.core, this.solr.host, undefined).subscribe({
next: (res: SolrResponse) => {
this.results = res.response.docs;
this.numFound = res.response.numFound;
// pagination
this.pagination["total"] = res.response.numFound;
this.pagination["perPage"] = res.responseHeader.params.rows as number;
this.pagination["currentPage"] = 1;
this.pagination["data"] = res.response.docs;
const facet_fields: FacetFields = res.facets;
let prop: keyof typeof facet_fields;
for (prop in facet_fields) {
const facetCategory: FacetInstance = facet_fields[prop];
if (facetCategory.buckets) {
const facetItems: Array<FacetItem> = facetCategory.buckets;
const facetValues = facetItems.map((facetItem) => {
let rObj: FacetItem;
if (this.facets[prop]?.some((e) => e.val === facetItem.val)) {
// console.log(facetValue + " is included")
const indexOfFacetValue = this.facets[prop].findIndex((i) => i.val === facetItem.val);
// console.log(indexOfFacetValue);
rObj = this.facets[prop][indexOfFacetValue];
rObj.count = facetItem.count;
// rObj = new FacetItem(val, count);
//if facet ccategory is reactivated category, deactivate all filter items
if (prop == categoryName) {
rObj.active = false;
// Trigger new search with updated filter
DatasetService.facetedSearchOPEN(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;
// Update pagination
this.pagination.total = res.hits.total.value;
this.pagination.perPage = 10;
this.pagination.currentPage = 1;
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));
const facetValues = facetItems.map((facetItem) => {
let rObj: FacetItem;
if (this.facets[prop]?.some((e) => e.val === facetItem.val)) {
// Update existing facet item with new count
const indexOfFacetValue = this.facets[prop].findIndex((i) => i.val === facetItem.val);
rObj = this.facets[prop][indexOfFacetValue];
rObj.count = facetItem.count;
// if facet category is reactivated category, deactivate all filter items
if (prop === categoryName) {
rObj.active = false;
}
} else {
// Create new facet item
rObj = new FacetItem(facetItem.val, facetItem.count);
}
} else {
rObj = new FacetItem(facetItem.val, facetItem.count);
}
return rObj;
});
this.facets[prop] = facetValues;
return rObj;
}).filter(el => el.count > 0); // Filter out items with count <= 0
this.facets[prop] = facetValues;
}
}
}
},
@ -311,5 +452,4 @@ export default class SearchViewComponent extends Vue {
});
}
// onPaging(page: number): void {}
}

View File

@ -1,31 +1,11 @@
<template>
<div id="page_style" class="rows site-content page__style page__description" autocomplete="off">
<div class="container-fluid banner mz-5">
<vs-input v-bind:propDisplay="searchTerm" v-bind:placeholder="'Enter your search term...'" @search-change="onSearch"></vs-input>
</div>
<div class="column is-half is-offset-one-quarter" style="padding-top: 0; margin-top: 0">
<!-- <div class="tabs is-centered">
<ul id="id-results-tabs">
<li class="search_tab is-active">
<a target="_self">Web</a>
</li>
<li class="search_tab">
<a target="_self">Images</a>
</li>
<li class="search_tab">
<a target="_self">Videos</a>
</li>
<li class="search_tab">
<a target="_self">Homepages</a>
</li>
<li class="search_tab">
<a target="_self">Food</a>
</li>
<li class="search_tab">
<a target="_self">Books</a>
</li>
</ul>
</div> -->
<div v-if="results.length > 0" class="result-list-info">
<div v-if="hasSearchTerm()" class="resultheader">
Your search term {{ "'" + stringSearchTerm + "'" }} yielded <strong>{{ numFound }}</strong> results:
@ -54,7 +34,7 @@
<active-facet-category
v-bind:filterItems="values"
v-bind:categoryName="key"
@clear-facet-category="onClearFacetCategory"
@clear-facet-category="onClearFacetCategoryOPEN"
></active-facet-category>
</span>
</div>

View File

@ -89,8 +89,11 @@ module.exports = {
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: "false",
APP_URL: JSON.stringify(process.env.APP_URL),
VUE_API: JSON.stringify(process.env.VUE_API),
SOLR_HOST: JSON.stringify(process.env.SOLR_HOST),
SOLR_CORE: JSON.stringify(process.env.SOLR_CORE),
// SOLR_HOST: JSON.stringify(process.env.SOLR_HOST),
// SOLR_CORE: JSON.stringify(process.env.SOLR_CORE),
// OPENSEARCH
OPEN_HOST: JSON.stringify(process.env.OPEN_HOST),
OPEN_CORE: JSON.stringify(process.env.OPEN_CORE),
}),
// new NodePolyfillPlugin(),
],