Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: persist search by example on page refresh #1606

Merged
merged 17 commits into from
Jul 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ import {
useSearchByExampleOptions,
extractResourceName
} from '@/page/data-explorer/search-by-example';
import { getResourceID } from '@/utils/data-util';

const props = defineProps<{
showSuggestions: boolean;
Expand All @@ -157,8 +158,7 @@ const selectedSearchByExampleOptions = ref<SearchByExampleOptions>({
backwardCitation: false,
relatedContent: false
});
const { searchByExampleOptions, searchByExampleItem, searchByExampleAssetCardProp } =
useSearchByExampleOptions();
const { searchByExampleOptions, searchByExampleItem } = useSearchByExampleOptions();

function clearQuery() {
query.value = '';
Expand All @@ -167,20 +167,21 @@ function clearQuery() {

const initiateSearch = () => {
emit('query-changed', query.value);
router.push({ name: RouteName.DataExplorerRoute, query: { q: query.value, byExample: 'false' } });
router.push({ name: RouteName.DataExplorerRoute, query: { q: query.value } });
EventService.create(EventType.Search, resources.activeProject?.id, query.value);
};

function initiateSearchByExample() {
searchByExampleItem.value = searchByExampleSelectedAsset.value;
searchByExampleOptions.value = { ...selectedSearchByExampleOptions.value };
searchByExampleToggle.value = false;

// used in order to update the "showing x of y results with ... to <Asset Card>"
// section after a search by example is initiated
searchByExampleAssetCardProp.value = { ...searchByExampleItem.value };
// used to update the search bar text with the name of the search by example asset
query.value = extractResourceName(searchByExampleItem.value);
router.push({ name: RouteName.DataExplorerRoute, query: { q: query.value, byExample: 'true' } });
router.push({
name: RouteName.DataExplorerRoute,
query: { resourceId: getResourceID(searchByExampleItem.value!) }
});
}

function addToQuery(term: string) {
Expand Down Expand Up @@ -230,7 +231,6 @@ function onDrop() {
searchByExampleToggle.value = true; // Maintains open search by example popup once an asset is successfully dropped
searchByExampleSelectedAsset.value = getDragData('asset');
searchByExampleSelectedResourceType.value = getDragData('resourceType');
searchByExampleItem.value = searchByExampleSelectedAsset.value;
isDraggedOver.value = false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@
<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
import TeraSliderPanel from '@/components/widgets/tera-slider-panel.vue';
import { fetchData, getXDDSets } from '@/services/data';
import { fetchData, getDocumentById, getXDDSets } from '@/services/data';
import {
ResourceType,
ResultType,
Expand Down Expand Up @@ -148,8 +148,7 @@ const route = useRoute();
const queryStore = useQueryStore();
const resources = useResourcesStore();

const { searchByExampleOptions, searchByExampleItem, searchByExampleAssetCardProp } =
useSearchByExampleOptions();
const { searchByExampleOptions, searchByExampleItem } = useSearchByExampleOptions();
const dataItems = ref<SearchResults[]>([]);
const dataItemsUnfiltered = ref<SearchResults[]>([]);
const selectedSearchItems = ref<ResultType[]>([]);
Expand Down Expand Up @@ -276,20 +275,12 @@ const executeSearch = async () => {
};

// handle the search-by-example for finding related documents, models, and/or datasets
if (
executeSearchByExample.value &&
(searchByExampleItem.value || searchByExampleAssetCardProp.value)
) {
const id = getResourceID(
searchByExampleItem.value ?? searchByExampleAssetCardProp.value
) as string;
if (executeSearchByExample.value && searchByExampleItem.value) {
const id = getResourceID(searchByExampleItem.value) as string;
//
// find related documents (which utilizes the xDD doc2vec API through the HMI server)
//
if (
isDocument(searchByExampleItem.value ?? searchByExampleAssetCardProp.value) &&
searchParams.xdd
) {
if (isDocument(searchByExampleItem.value) && searchParams.xdd) {
searchParams.xdd.dataset = xddDataset.value;
if (searchByExampleOptions.value.similarContent) {
searchParams.xdd.similar_search_enabled = executeSearchByExample.value;
Expand All @@ -303,21 +294,15 @@ const executeSearch = async () => {
//
// find related models (which utilizes the TDS provenance API through the HMI server)
//
if (
isModel(searchByExampleItem.value ?? searchByExampleAssetCardProp.value) &&
searchParams.model
) {
if (isModel(searchByExampleItem.value) && searchParams.model) {
searchParams.model.related_search_enabled = executeSearchByExample.value;
searchParams.model.related_search_id = id;
searchType = ResourceType.MODEL;
}
//
// find related datasets (which utilizes the TDS provenance API through the HMI server)
//
if (
isDataset(searchByExampleItem.value ?? searchByExampleAssetCardProp.value) &&
searchParams.dataset
) {
if (isDataset(searchByExampleItem.value) && searchParams.dataset) {
searchParams.dataset.related_search_enabled = executeSearchByExample.value;
searchParams.dataset.related_search_id = id;
searchType = ResourceType.DATASET;
Expand Down Expand Up @@ -406,7 +391,7 @@ const executeSearch = async () => {
};

const clearSearchByExampleSelections = () => {
// clear out the serch by example option selections
// clear out the search by example option selections
searchByExampleOptions.value = {
similarContent: false,
forwardCitation: false,
Expand Down Expand Up @@ -440,7 +425,6 @@ const onSearchByExample = async (searchOptions: SearchByExampleOptions) => {

await executeSearch();

searchByExampleItem.value = null;
dirtyResults.value[resourceType.value] = false;
}
};
Expand Down Expand Up @@ -556,6 +540,16 @@ async function executeNewQuery() {
dirtyResults.value[resourceType.value] = false;
}

async function searchByExampleOnPageRefresh(resourceId: string) {
if (!searchByExampleItem.value) {
searchByExampleItem.value = await getDocumentById(resourceId);
}
if (!Object.values(searchByExampleOptions.value).some((v) => v)) {
searchByExampleOptions.value.similarContent = true;
}
onSearchByExample(searchByExampleOptions.value);
}

// this is called whenever the user apply some facet filter(s)
watch(clientFilters, async (n, o) => {
if (filtersUtil.isEqual(n, o)) return;
Expand All @@ -579,15 +573,19 @@ watch(clientFilters, async (n, o) => {
watch(
() => route.query,
() => {
// Adding another query param 'byExample' for what should be a better way to determine whether we are searching by example or not.
// For now this is just a boolean string but this can be looked into further to maybe add additional parameters when searching by example.
// i.e. refreshing will land the user on the page with the example resource type already populated and used to search
if (route.query.byExample !== 'true') executeNewQuery();
// The query changes in the following cases:
// - when a user does a normal search - there is no `resourceId`
// - when a user does a search by example - there is a `resourceId`
// - when a user navigates back and forth on a page

if (route.query.resourceId) {
searchByExampleOnPageRefresh(route.query.resourceId.toString());
} else {
executeNewQuery();
}
}
);

watch(searchByExampleOptions, () => onSearchByExample(searchByExampleOptions.value));

// Default query on reload
onMounted(async () => {
xddDatasets.value = await getXDDSets();
Expand All @@ -597,7 +595,14 @@ onMounted(async () => {
} else {
resources.setXDDDataset('xdd-covid-19'); // give xdd dataset a default value
}
executeNewQuery();

// On reload, if the url has a resourceId, we know that a user just did a search by example
// so we want to preserve the search by example so we perform a search by example instead of a normal search
if (route.query.resourceId) {
searchByExampleOnPageRefresh(route.query.resourceId.toString());
} else {
executeNewQuery();
}
});

onUnmounted(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
{{ resourceType.toUpperCase() }}
<div
class="asset-filters"
v-if="resourceType === ResourceType.XDD && asset.knownEntities?.askemObjects"
v-if="resourceType === ResourceType.XDD && (asset as Document).knownEntities?.askemObjects"
>
<template
v-for="icon in [
Expand All @@ -28,20 +28,25 @@
/>
</template>
</div>
<div v-else-if="resourceType === ResourceType.MODEL">{{ asset.schema_name }}</div>
<div v-else-if="resourceType === ResourceType.MODEL">
{{ (asset as Model).schema_name }}
</div>
</div>
<header class="title" v-html="title" />
<div class="details" v-html="formatDetails" />
<ul class="snippets" v-if="snippets">
<li v-for="(snippet, index) in snippets" :key="index" v-html="snippet" />
</ul>
<div class="description" v-html="highlightSearchTerms(asset.description)" />
<div
class="description"
v-html="highlightSearchTerms((asset as Model | Dataset).description)"
/>
<div
class="parameters"
v-if="resourceType === ResourceType.MODEL && asset?.semantics?.ode?.parameters"
v-if="resourceType === ResourceType.MODEL && (asset as Model).semantics?.ode?.parameters"
>
PARAMETERS:
{{ asset.semantics.ode.parameters }}
{{ (asset as Model).semantics?.ode.parameters }}
<!--may need a formatting function this attribute is always undefined at the moment-->
</div>
<div class="features" v-else-if="resourceType === ResourceType.DATASET">
Expand All @@ -51,7 +56,9 @@
<footer><!--pill tags if already in another project--></footer>
</main>
<aside class="preview-and-options">
<figure v-if="resourceType === ResourceType.XDD && asset.knownEntities?.askemObjects">
<figure
v-if="resourceType === ResourceType.XDD && (asset as Document).knownEntities?.askemObjects"
>
<template v-if="relatedAsset">
<img
v-if="relatedAsset.properties.image"
Expand Down Expand Up @@ -105,7 +112,7 @@ import { watch, ref, computed, ComputedRef } from 'vue';
import { isEmpty } from 'lodash';
import { XDDExtractionType } from '@/types/XDD';
import { Document, Extraction, XDDUrlExtraction, Dataset, Model } from '@/types/Types';
import { ResourceType } from '@/types/common';
import { ResourceType, ResultType } from '@/types/common';
import * as textUtil from '@/utils/text';
import { useDragEvent } from '@/services/drag-drop';

Expand All @@ -116,7 +123,7 @@ type UrlExtraction = {
};

const props = defineProps<{
asset: Document & Model & Dataset;
asset: ResultType;
resourceType: ResourceType;
highlight?: string;
}>();
Expand All @@ -137,8 +144,8 @@ const chosenExtractionFilter = ref<XDDExtractionType | 'Asset'>('Asset');
const urlExtractions = computed(() => {
const urls: UrlExtraction[] = [];

if (props.asset.knownEntities.askemObjects) {
const documentsWithUrls = props.asset.knownEntities.askemObjects.filter(
if ((props.asset as Document).knownEntities.askemObjects) {
const documentsWithUrls = (props.asset as Document).knownEntities.askemObjects.filter(
(ex) =>
ex.askemClass === XDDExtractionType.Doc &&
ex.properties.documentBibjson?.knownEntities &&
Expand All @@ -162,9 +169,9 @@ const urlExtractions = computed(() => {
});

const extractions: ComputedRef<UrlExtraction[] & Extraction[]> = computed(() => {
if (props.asset.knownEntities.askemObjects) {
if ((props.asset as Document).knownEntities.askemObjects) {
const allExtractions = [
...(props.asset.knownEntities.askemObjects as UrlExtraction[] & Extraction[]),
...((props.asset as Document).knownEntities.askemObjects as UrlExtraction[] & Extraction[]),
...(urlExtractions.value as UrlExtraction[] & Extraction[])
];

Expand All @@ -177,10 +184,15 @@ const extractions: ComputedRef<UrlExtraction[] & Extraction[]> = computed(() =>

const relatedAsset = computed(() => extractions.value[relatedAssetPage.value]);
const snippets = computed(() =>
props.asset.highlight ? Array.from(props.asset.highlight).splice(0, 3) : null
(props.asset as Document).highlight
? Array.from((props.asset as Document).highlight).splice(0, 3)
: null
);
const title = computed(() => {
const value = props.resourceType === ResourceType.XDD ? props.asset.title : props.asset.name;
const value =
props.resourceType === ResourceType.XDD
? (props.asset as Document).title
: (props.asset as Model | Dataset).name;
return highlightSearchTerms(value);
});

Expand Down Expand Up @@ -224,14 +236,14 @@ function updateExtractionFilter(extractionType: XDDExtractionType) {
// Return formatted author, year, journal
const formatDetails = computed(() => {
if (props.resourceType === ResourceType.XDD) {
const details = `${props.asset.author.map((a) => a.name).join(', ')} (${props.asset.year}) ${
props.asset.journal
}`;
const details = `${(props.asset as Document).author.map((a) => a.name).join(', ')} (${
(props.asset as Document).year
}) ${(props.asset as Document).journal}`;
return highlightSearchTerms(details);
}

if (props.resourceType === ResourceType.DATASET) {
return props.asset?.url;
return (props.asset as Dataset).url;
}

return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { ResultType, ResourceType } from '@/types/common';
import TeraAssetCard from '@/page/data-explorer/components/tera-asset-card.vue';

const props = defineProps<{
asset: Document & Model & Dataset;
asset: ResultType;
isPreviewed: boolean;
resourceType: ResourceType;
selectedSearchItems: ResultType[];
Expand All @@ -36,15 +36,15 @@ const isSelected = () =>
props.selectedSearchItems.find((item) => {
if (isDocument(item)) {
const itemAsDocument = item as Document;
return itemAsDocument.title === props.asset.title;
return itemAsDocument.title === (props.asset as Document).title;
}
if (isDataset(item)) {
const itemAsDataset = item as Dataset;
return itemAsDataset.id === props.asset.id;
return itemAsDataset.id === (props.asset as Dataset).id;
}
if (isModel(item)) {
const itemAsModel = item as Model;
return itemAsModel.id === props.asset.id;
return itemAsModel.id === (props.asset as Model).id;
}
return false;
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
<div class="result-details">
<span class="result-count">
<template v-if="isLoading">Loading...</template>
<template v-else-if="props.searchTerm">
<template v-else-if="props.searchTerm || searchByExampleOptionsStr">
{{ resultsText }}
<span v-if="searchByExampleOptionsStr.length === 0"> "{{ props.searchTerm }}" </span>
<div v-else-if="searchByExampleOptionsStr.length > 0" class="search-by-example-card">
<tera-asset-card
:asset="searchByExampleAssetCardProp"
:asset="searchByExampleItem!"
:resource-type="(resultType as ResourceType)"
/>
</div>
Expand Down Expand Up @@ -53,7 +53,7 @@
</template>

<script setup lang="ts">
import { ref, computed, PropType, onUnmounted } from 'vue';
import { ref, computed, PropType } from 'vue';
import { Document, XDDFacetsItemResponse, Dataset, Model } from '@/types/Types';
import useQueryStore from '@/stores/query';
import { SearchResults, ResourceType, ResultType } from '@/types/common';
Expand All @@ -66,7 +66,7 @@ import {
} from '@/page/data-explorer/search-by-example';
import TeraSearchItem from './tera-search-item.vue';

const { searchByExampleAssetCardProp } = useSearchByExampleOptions();
const { searchByExampleItem } = useSearchByExampleOptions();

const props = defineProps({
dataItems: {
Expand Down Expand Up @@ -182,10 +182,6 @@ const itemsText = computed(() => {
const s = resultsCount.value === 1 ? '' : 's';
return `Showing ${resultsCount.value} ${truncated}item${s}.`;
});

onUnmounted(() => {
searchByExampleAssetCardProp.value = null;
});
</script>

<style scoped>
Expand Down
Loading