OpenSearch - some progress in migration
This commit is contained in:
parent
a853e93b68
commit
6d1c1b28c3
|
@ -1,15 +1,34 @@
|
||||||
import initializeAxios from "./axiosSetup";
|
// Import the necessary modules and functions
|
||||||
import { axiosRequestConfiguration } from "./config";
|
import initializeAxios from "./axiosSetup"; // Function to initialize the Axios instance
|
||||||
import { map } from "rxjs/operators";
|
import { axiosRequestConfiguration } from "./config"; // Axios configuration settings
|
||||||
import { defer, Observable } from "rxjs";
|
import { map } from "rxjs/operators"; // Operator to transform the items emitted by an Observable
|
||||||
import { AxiosResponse } from "axios";
|
import { defer, Observable } from "rxjs"; // RxJS utilities for creating and working with Observables
|
||||||
// https://ichi.pro/de/so-wickeln-sie-axios-mit-typescript-und-react-in-rxjs-ein-118892823169891
|
import { AxiosResponse } from "axios"; // Axios response type
|
||||||
|
|
||||||
|
// Initialize the Axios instance with the provided configuration
|
||||||
const axiosInstance = initializeAxios(axiosRequestConfiguration);
|
const axiosInstance = initializeAxios(axiosRequestConfiguration);
|
||||||
|
|
||||||
|
// Function to make a GET request using Axios wrapped in an Observable
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
const get = <T>(url: string, queryParams?: any): Observable<T> => {
|
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));
|
||||||
};
|
};
|
||||||
|
|
||||||
export default { get };
|
// 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
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
// Export the get function as part of the default export
|
||||||
|
export default { get, post };
|
|
@ -89,6 +89,8 @@ export default class VsInput extends Vue {
|
||||||
const suggestions = new Array<Suggestion>();
|
const suggestions = new Array<Suggestion>();
|
||||||
|
|
||||||
console.log("Display:", this.display);
|
console.log("Display:", this.display);
|
||||||
|
// console.log("results:", this.results );
|
||||||
|
|
||||||
|
|
||||||
this.results.forEach((dataset) => {
|
this.results.forEach((dataset) => {
|
||||||
|
|
||||||
|
@ -106,23 +108,23 @@ export default class VsInput extends Vue {
|
||||||
suggestions.push(suggestion);
|
suggestions.push(suggestion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.find(dataset.author, this.display.toLowerCase()) !== "") {
|
// if (this.find(dataset.author, this.display.toLowerCase()) !== "") {
|
||||||
const author = this.find(dataset.author, this.display.toLowerCase());
|
// const author = this.find(dataset.author, this.display.toLowerCase());
|
||||||
|
|
||||||
const hasAuthorSuggestion = suggestions.some((suggestion) => suggestion.value === author && suggestion.type == SearchType.Author);
|
// const hasAuthorSuggestion = suggestions.some((suggestion) => suggestion.value === author && suggestion.type == SearchType.Author);
|
||||||
if (!hasAuthorSuggestion) {
|
// if (!hasAuthorSuggestion) {
|
||||||
const suggestion = new Suggestion(author, SearchType.Author);
|
// const suggestion = new Suggestion(author, SearchType.Author);
|
||||||
suggestions.push(suggestion);
|
// suggestions.push(suggestion);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
if (this.find(dataset.subject, this.display.toLowerCase()) != "") {
|
// if (this.find(dataset.subject, this.display.toLowerCase()) != "") {
|
||||||
const subject = 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);
|
// const hasSubjectSuggestion = suggestions.some((suggestion) => suggestion.value === subject && suggestion.type == SearchType.Subject);
|
||||||
if (!hasSubjectSuggestion) {
|
// if (!hasSubjectSuggestion) {
|
||||||
const suggestion = new Suggestion(subject, SearchType.Subject);
|
// const suggestion = new Suggestion(subject, SearchType.Subject);
|
||||||
suggestions.push(suggestion);
|
// suggestions.push(suggestion);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
});
|
});
|
||||||
|
|
||||||
return suggestions;
|
return suggestions;
|
||||||
|
@ -174,7 +176,8 @@ export default class VsInput extends Vue {
|
||||||
|
|
||||||
private request(): void {
|
private request(): void {
|
||||||
console.log("request()");
|
console.log("request()");
|
||||||
DatasetService.searchTerm(this.display, this.solr.core, this.solr.host).subscribe({
|
// DatasetService.searchTerm(this.display, this.solr.core, this.solr.host).subscribe({
|
||||||
|
DatasetService.searchTerm(this.display).subscribe({
|
||||||
next: (res: Dataset[]) => this.dataHandler(res),
|
next: (res: Dataset[]) => this.dataHandler(res),
|
||||||
error: (error: string) => this.errorHandler(error),
|
error: (error: string) => this.errorHandler(error),
|
||||||
complete: () => (this.loading = false),
|
complete: () => (this.loading = false),
|
||||||
|
|
|
@ -7,14 +7,6 @@ export interface SolrResponse {
|
||||||
// facet_counts: FacetCount;
|
// facet_counts: FacetCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
// OPENSEARCH
|
|
||||||
export interface OpenResponse {
|
|
||||||
responseHeader: ResponseHeader;
|
|
||||||
response: ResponseContent;
|
|
||||||
facets: FacetFields;
|
|
||||||
// facet_counts: FacetCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ResponseHeader {
|
export interface ResponseHeader {
|
||||||
status: boolean;
|
status: boolean;
|
||||||
QTime: number;
|
QTime: number;
|
||||||
|
@ -79,3 +71,51 @@ export class FacetItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
// OPENSEARCH
|
||||||
|
// ========================================================================
|
||||||
|
export interface OpenSearchResponse {
|
||||||
|
took: number;
|
||||||
|
timed_out: boolean;
|
||||||
|
_shards: Shards;
|
||||||
|
hits: Hits;
|
||||||
|
aggregations?: Aggregations;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
relation: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Hit {
|
||||||
|
_index: string;
|
||||||
|
_id: string;
|
||||||
|
_score: number;
|
||||||
|
_source: Dataset;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Aggregations {
|
||||||
|
[key: string]: Aggregation;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Aggregation {
|
||||||
|
buckets: Array<Bucket>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Bucket {
|
||||||
|
key: string;
|
||||||
|
doc_count: number;
|
||||||
|
}
|
||||||
|
|
|
@ -3,44 +3,12 @@ import api from "../api/api";
|
||||||
import { Observable } from "rxjs";
|
import { Observable } from "rxjs";
|
||||||
import { map } from "rxjs/operators";
|
import { map } from "rxjs/operators";
|
||||||
import { Dataset, DbDataset, Suggestion } from "@/models/dataset";
|
import { Dataset, DbDataset, Suggestion } from "@/models/dataset";
|
||||||
import { SolrResponse } from "@/models/headers";
|
import { OpenSearchResponse, SolrResponse } from "@/models/headers";
|
||||||
import { ActiveFilterCategories } from "@/models/solr";
|
import { ActiveFilterCategories } from "@/models/solr";
|
||||||
import { VUE_API } from "@/constants";
|
import { VUE_API } from "@/constants";
|
||||||
import { deserialize } from "class-transformer";
|
import { deserialize } from "class-transformer";
|
||||||
|
|
||||||
class DatasetService {
|
class DatasetService {
|
||||||
// /* Initial test method to fetch and log data from the local OpenSearch endpoint (new backend) */
|
|
||||||
// async fetchDataFromOpenSearch(searchTerm: string): Promise<void> {
|
|
||||||
// const url = "http://192.168.21.18/tethys-records/_search";
|
|
||||||
// const headers = {
|
|
||||||
// "Content-Type": "application/json",
|
|
||||||
// };
|
|
||||||
// const body = {
|
|
||||||
// query: {
|
|
||||||
// match: {
|
|
||||||
// title: searchTerm,
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// };
|
|
||||||
|
|
||||||
// try {
|
|
||||||
// const response = await fetch(url, {
|
|
||||||
// method: "POST",
|
|
||||||
// headers: headers,
|
|
||||||
// body: JSON.stringify(body),
|
|
||||||
// });
|
|
||||||
|
|
||||||
// if (!response.ok) {
|
|
||||||
// throw new Error(`Failed to fetch data from ${url}, status: ${response.status}`);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const data = await response.json();
|
|
||||||
// console.log("Data from OpenSearch:", data);
|
|
||||||
// } catch (error) {
|
|
||||||
// console.error("Error fetching data:", error);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch data from the OpenSearch endpoint with fuzzy search enabled.
|
* Fetch data from the OpenSearch endpoint with fuzzy search enabled.
|
||||||
* This function allows for misspellings in the search term and boosts
|
* This function allows for misspellings in the search term and boosts
|
||||||
|
@ -50,7 +18,7 @@ class DatasetService {
|
||||||
*/
|
*/
|
||||||
async fetchDataFromOpenSearch(searchTerm: string): Promise<void> {
|
async fetchDataFromOpenSearch(searchTerm: string): Promise<void> {
|
||||||
// Define the OpenSearch endpoint URL
|
// Define the OpenSearch endpoint URL
|
||||||
const url = "http://192.168.21.18/tethys-records/_search";
|
const url = "http://opensearch.geoinformation.dev/tethys-records/_search";
|
||||||
|
|
||||||
// Set the headers for the POST request
|
// Set the headers for the POST request
|
||||||
const headers = {
|
const headers = {
|
||||||
|
@ -149,68 +117,6 @@ class DatasetService {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// // Construct the body of the POST request
|
|
||||||
// const body = {
|
|
||||||
// query: {
|
|
||||||
// bool: {
|
|
||||||
// // The `should` clause specifies that at least one of these conditions must match
|
|
||||||
// should: [
|
|
||||||
// {
|
|
||||||
// // Match the search term in the title field with a wildcard
|
|
||||||
// wildcard: {
|
|
||||||
// title: {
|
|
||||||
// value: `${searchTerm}*`, // Wildcard search for terms starting with searchTerm
|
|
||||||
// boost: 3 // Boosting the relevance of title matches
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// // Match the search term in the author field with a wildcard
|
|
||||||
// wildcard: {
|
|
||||||
// author: {
|
|
||||||
// value: `${searchTerm}*`, // Wildcard search for terms starting with searchTerm
|
|
||||||
// boost: 2 // Boosting the relevance of author matches
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// // Match the search term in the subject field with a wildcard
|
|
||||||
// wildcard: {
|
|
||||||
// subject: {
|
|
||||||
// value: `${searchTerm}*`, // Wildcard search for terms starting with searchTerm
|
|
||||||
// boost: 1 // Boosting the relevance of subject matches
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// ],
|
|
||||||
// // Ensure that at least one of the `should` clauses must match
|
|
||||||
// minimum_should_match: 1
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// // Limit the number of search results to 10
|
|
||||||
// size: 10,
|
|
||||||
// // Start from the first result (pagination)
|
|
||||||
// from: 0,
|
|
||||||
// // Sort the results by the `server_date_published` field in descending order
|
|
||||||
// sort: [
|
|
||||||
// { server_date_published: { order: "desc" } }
|
|
||||||
// ],
|
|
||||||
// // Aggregations to provide facets for the `language` and `subject` fields
|
|
||||||
// aggs: {
|
|
||||||
// language: {
|
|
||||||
// terms: {
|
|
||||||
// field: "language.keyword" // Aggregate by the exact values of the `language` field
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// subject: {
|
|
||||||
// terms: {
|
|
||||||
// field: "subjects.keyword", // Aggregate by the exact values of the `subjects` field
|
|
||||||
// size: 10 // Limit the number of aggregation buckets to 10
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Send the POST request to the OpenSearch endpoint
|
// Send the POST request to the OpenSearch endpoint
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
|
@ -241,10 +147,41 @@ class DatasetService {
|
||||||
terms%22%2C%20field%3A%20%22year%22%20%7D&json.facet.author=%7B%20type%3A%20%22terms%22%2C%20field%3A%20%22author_facet%22%2C%20limit%3A%20-1%20%7D
|
terms%22%2C%20field%3A%20%22year%22%20%7D&json.facet.author=%7B%20type%3A%20%22terms%22%2C%20field%3A%20%22author_facet%22%2C%20limit%3A%20-1%20%7D
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
private openSearchUrl = "http://opensearch.geoinformation.dev/tethys-records/_search";
|
||||||
|
|
||||||
|
public searchTerm(term: string): Observable<Dataset[]> {
|
||||||
|
const body = {
|
||||||
|
query: {
|
||||||
|
bool: {
|
||||||
|
should: [
|
||||||
|
{ match: { title: { query: term, fuzziness: "AUTO", boost: 3 } } },
|
||||||
|
{ match: { author: { query: term, fuzziness: "AUTO", boost: 2 } } },
|
||||||
|
{ match: { subject: { query: term, fuzziness: "AUTO", boost: 1 } } },
|
||||||
|
{ wildcard: { title: { value: `${term}*`, boost: 3 } } },
|
||||||
|
{ wildcard: { author: { value: `${term}*`, boost: 2 } } },
|
||||||
|
{ wildcard: { subject: { value: `${term}*`, boost: 1 } } }
|
||||||
|
],
|
||||||
|
minimum_should_match: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
size: 10,
|
||||||
|
from: 0,
|
||||||
|
sort: [{ server_date_published: { order: "desc" } }],
|
||||||
|
aggs: {
|
||||||
|
language: { terms: { field: "language.keyword" } },
|
||||||
|
subject: { terms: { field: "subjects.keyword", size: 10 } }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return api.post<OpenSearchResponse>(this.openSearchUrl, body).pipe(
|
||||||
|
map(response => response.hits.hits.map(hit => hit._source))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// For the autocomplete search. Method to perform a search based on a term
|
// For the autocomplete search. Method to perform a search based on a term
|
||||||
public searchTerm(term: string, solrCore: string, solrHost: string): Observable<Dataset[]> {
|
public searchTerm_SOLR(term: string, solrCore: string, solrHost: string): Observable<Dataset[]> {
|
||||||
// Calling the test method for
|
// Calling the test method for
|
||||||
this.fetchDataFromOpenSearch(term);
|
// this.fetchDataFromOpenSearch(term);
|
||||||
// solr endpoint
|
// solr endpoint
|
||||||
const host = "https://" + solrHost;
|
const host = "https://" + solrHost;
|
||||||
const path = "/solr/" + solrCore + "/select?";
|
const path = "/solr/" + solrCore + "/select?";
|
||||||
|
@ -276,6 +213,11 @@ class DatasetService {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Make API call to Solr and return the result
|
// 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));
|
const stations = api.get<SolrResponse>(base, q_params).pipe(map((res: SolrResponse) => res.response.docs));
|
||||||
|
|
||||||
return stations;
|
return stations;
|
||||||
|
@ -290,7 +232,9 @@ class DatasetService {
|
||||||
&json.facet.year=%7B%20type%3A%20%22terms%22%2C%20field%3A%20%22year%22%20%7D
|
&json.facet.year=%7B%20type%3A%20%22terms%22%2C%20field%3A%20%22year%22%20%7D
|
||||||
&json.facet.author=%7B%20type%3A%20%22terms%22%2C%20field%3A%20%22author_facet%22%2C%20limit%3A%20-1%20%7D */
|
&json.facet.author=%7B%20type%3A%20%22terms%22%2C%20field%3A%20%22author_facet%22%2C%20limit%3A%20-1%20%7D */
|
||||||
|
|
||||||
// Method to perform a faceted search
|
/**
|
||||||
|
* 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(
|
public facetedSearch(
|
||||||
suggestion: Suggestion | string,
|
suggestion: Suggestion | string,
|
||||||
activeFilterCategories: ActiveFilterCategories,
|
activeFilterCategories: ActiveFilterCategories,
|
||||||
|
@ -316,13 +260,13 @@ class DatasetService {
|
||||||
"doctype",
|
"doctype",
|
||||||
].toString();
|
].toString();
|
||||||
|
|
||||||
// Determine search term, query operator, and query fields based on the suggestion type
|
// 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;
|
let term, queryOperator, qfFields;
|
||||||
if (typeof suggestion === "string") {
|
if (typeof suggestion === "string") { // f suggestion is a string, it appends a wildcard (*) for partial matches.
|
||||||
term = suggestion + "*";
|
term = suggestion + "*";
|
||||||
queryOperator = "or";
|
queryOperator = "or";
|
||||||
qfFields = "title^3 author^2 subject^1";
|
qfFields = "title^3 author^2 subject^1";
|
||||||
} else if (suggestion instanceof Suggestion) {
|
} 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 + '"';
|
term = suggestion.type + ':"' + suggestion.value + '"';
|
||||||
queryOperator = "and";
|
queryOperator = "and";
|
||||||
qfFields = undefined;
|
qfFields = undefined;
|
||||||
|
|
|
@ -10,7 +10,7 @@ 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 { 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 { OpenResponse, SolrResponse, FacetFields, FacetItem, FacetResults, FacetInstance } from "@/models/headers";
|
import { SolrResponse, FacetFields, FacetItem, FacetResults, FacetInstance } from "@/models/headers";
|
||||||
import { ActiveFilterCategories } from "@/models/solr";
|
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 { IPagination } from "@/models/pagination";
|
||||||
|
|
Loading…
Reference in New Issue
Block a user