Unable to use multi-select and its giving error
Les mainteneurs répondent en général sous 1 jour
Personne n'a encore pris cette issue.
Évaluation
- Difficulté
- 4/5
- Temps estimé
- 3-5 jours
- Accessibilité débutants
- 25/100
- Type d'issue
- Bug
- Clarté
- À clarifier
- Activité
- À l'abandon
- Stack technique
- elasticsearch, javascript
Piste de recherche
Commencez par l’en-tête registrants fourni et le composant parent, en vous concentrant sur les contrôles multi-list et data-search du champ email. Examinez la stack trace de query.js et reproduisez la requête de filtrage afin d’identifier pourquoi la requête générée effectue une opération non prise en charge sur email. C’est terminé lorsque le filtrage multi-select ne renvoie plus l’erreur 400 d’Elasticsearch affichée.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Description
Affected Projects
Vue.JS
Library Version: x.y.z
"@appbaseio/reactivesearch-vue": "3.1.0-alpha.1",
Describe the bug
I am migrating our app from vue 2 to vue 3, so also need to migrate appbase. there is is migration docs which we following. everything seems work but when we filter it gives this error
bugsnag.js:2688 {"settings":{"took":6,"searchTook":2},"SearchResults":{"error":{"root_cause":[{"type":"illegal_argument_exception","reason":"Text fields are not optimised for operations that require per-document field data like aggregations and sorting, so these operations are disabled by default. Please use a keyword field instead. Alternatively, set fielddata=true on [email] in order to load field data by uninverting the inverted index. Note that this can use significant memory."}],"type":"search_phase_execution_exception","reason":"all shards failed","phase":"query","grouped":true,"failed_shards":[{"shard":0,"index":"4760d3c8-ab04-450d-b83c-83ff9bdc1d0d","node":"Fa2quZrSSlCuIwgA2TZRVg","reason":{"type":"illegal_argument_exception","reason":"Text fields are not optimised for operations that require per-document field data like aggregations and sorting, so these operations are disabled by default. Please use a keyword field instead. Alternatively, set fielddata=true on [email] in order to load field data by uninverting the inverted index. Note that this can use significant memory."}}],"caused_by":{"type":"illegal_argument_exception","reason":"Text fields are not optimised for operations that require per-document field data like aggregations and sorting, so these operations are disabled by default. Please use a keyword field instead. Alternatively, set fielddata=true on [email] in order to load field data by uninverting the inverted index. Note that this can use significant memory.","caused_by":{"type":"illegal_argument_exception","reason":"Text fields are not optimised for operations that require per-document field data like aggregations and sorting, so these operations are disabled by default. Please use a keyword field instead. Alternatively, set fielddata=true on [email] in order to load field data by uninverting the inverted index. Note that this can use significant memory."}}},"status":400}}
console.<computed> @ bugsnag.js:2688
handleError @ utils.js:1
eval @ query.js:1
Desktop (please complete the following information):
- Browser [e.g. chrome]
here is my component code
<template>
<!-- use v-show here because the popup can still stay open while the page is loading -->
<tr>
<th
v-for="header of headers"
:key="header.text"
class="tw-border-r tw-border-r-slate-200 !tw-border-b-slate-200 !tw-bg-slate-100 !tw-min-w-[100px]"
>
<div class="tw-flex tw-items-center tw-justify-between">
<div class="tw-mr-1 tw-text-sm tw-font-semibold tw-text-slate-600" :class="['text', { hidden: !header.text }]">
{{ header.text }}
</div>
<div v-if="!header.text" class="tw-flex tw-flex-1 tw-items-center tw-justify-center">
<input
type="checkbox"
class="tw-h-4 tw-w-4 tw-rounded tw-border-solid tw-border-slate-500 tw-text-indigo-600 focus:tw-ring-indigo-500 dark:tw-bg-black"
:checked="allSelected"
@change="toggleColumnSelected"
/>
</div>
<div v-else-if="header.value == 'user__magic_link' || header.value == 'date_registered'" />
<v-menu v-else :modelValue="headerPopups[header.value]" :closeOnContentClick="false">
<template #activator="{ props }">
<button class="tw-h-full tw-mt-1" v-bind="props">
<MagnifyingGlass v-if="header.value == 'full_name' || header.value == 'email'" class="!tw-h-5 !tw-w-5" />
<Funnel v-else class="!tw-h-5 !tw-w-5" />
</button>
</template>
<div class="tw-bg-white tw-rounded tw-flex tw-items-center !tw-p-2">
<multi-list
v-if="header.value === 'role'"
componentId="role"
dataField="role"
:innerClass="{
list: '!tw-p-0'
}"
>
<template #renderItem="{ label }">
<div class="tw-flex tw-text-sm tw-space-2">
{{ $store.state.registration.roleNames[label] }}
<img
v-if="$store.state.registration.roleIcons[label]"
:src="$store.state.registration.roleIcons[label]"
style="width: 13px"
/>
</div>
</template>
</multi-list>
<data-search
v-else-if="header.value === 'full_name'"
queryFormat="and"
componentId="full_name"
className="inlineSearch"
:dataField="['full_name']"
/>
<data-search
v-else-if="header.value === 'email'"
queryFormat="and"
componentId="email"
className="inlineSearch"
:dataField="['email']"
/>
<multi-list
v-else
:componentId="header.value"
:dataField="`${header.value}.keyword`"
title=""
:showCount="false"
:size="500"
:innerClass="{
list: '!tw-p-0'
}"
>
<template #renderItem="{ label }">
<div class="tw-text-sm">
{{ label }}
</div>
</template>
</multi-list>
</div>
</v-menu>
</div>
</th>
</tr>
</template>
<script>
import MagnifyingGlass from '@/studioComponents/Icons/MagnifyingGlass.vue';
import Funnel from '@/studioComponents/Icons/Funnel.vue';
import { mapGetters, mapMutations, mapState } from 'vuex';
export default {
name: 'StudioRegistrantsTableHeader',
components: { MagnifyingGlass, Funnel },
props: {
headers: {
type: Array,
default: () => []
}
},
data() {
const headerPopups = this.headers
.map(it => it.value)
.reduce((acuum, item) => {
acuum[item] = false;
return acuum;
}, {});
return {
headerPopups
};
},
computed: {
...mapState('registration', ['allSelected', 'lastSearchedQueryResultSize']),
...mapGetters('registration', ['numSelected'])
},
methods: {
...mapMutations('registration', ['toggleSelectAll']),
toggleColumnSelected() {
this.toggleSelectAll(!this.allSelected);
}
}
};
</script>
<style lang="scss">
.inlineSearch {
#full_name-downshift {
height: 160px;
}
#email-downshift {
height: 160px;
}
}
.v-menu__content {
background: white;
}
</style>
Parent component
<template>
<div class="tw-flex tw-flex-col tw-px-4">
<StudioPageIntro title="Registrants">
Register attendees for your event and control access for each user. Registrants will automatically be sent email
notifications once added, so confirm your email schedule before uploading registrants.
</StudioPageIntro>
<div class="tw-flex tw-flex-1 tw-flex-col tw-overflow-scroll tw-w-full">
<!--
This is a very frustrating component because of reactive-base. If you make any chnages,
you can start having problems with the components rendered by appbase, such as filters
not updating or the pagination changing back to page 1 randomly.
It seems reactive-base observes all its direct children and on any change that will
re-render the virtual dom of its direct descendants it will refresh. It will also
stop refreshing when it is supposed to refresh if its not a direct descendant.
A rule of thumb, if you want it to make the data update, make it a direct child. If you
want it to not make the reactive-base store refresh, put it as a descendant of a descendant
and then provide all the data it needs through the store instead of through props.
-->
<StudioSpinner v-if="loadingColumns" overlay />
<reactive-base v-else :app="eventId" :credentials="appbaseCredentials" :url="appBaseUrl">
<StudioSpinner v-show="loadingRegistration" overlay />
<div>
<div class="tw-flex tw-flex-row tw-py-3 tw-align-top">
<div class="tw-flex tw-flex-row tw-flex-1 tw-justify-start tw-items-start tw-mt-1">
<button class="hover:tw-opacity-80 tw-mr-2 tw-mt-[2px]" @click="refreshList">
<Refresh class="tw-w-5 tw-h-5 tw-text-indigo-600 tw-transition-transform" />
</button>
<div v-if="summary" class="tw-mr-2">
{{ summary }}
</div>
</div>
<div class="tw-flex tw-flex-row tw-justify-end tw-items-start tw-px-2">
<search-box
placeholder="Search Registrants..."
className="appbase-search"
:innerClass="{
list: 'search-bar-list'
}"
queryFormat="and"
filterLabel="Search"
componentId="SearchUsers"
:dataField="['full_name']"
/>
<StudioAddRegistrant @onRegistrantAdd="searchRegistrants" />
</div>
</div>
<selected-filters :showClearAll="true">
<template #default="{ selectedValues, setValue, clearValues }">
<div class="tw-flex tw-flex-row tw-flex-wrap">
<Chip
v-for="componentId in Object.keys(getFilteredValues(selectedValues))"
:key="componentId"
class="tw-mr-2"
:borderRadius="16"
:label="`${filteredText(componentId)}: ${selectedFilterValue(componentId, selectedValues)}`"
allowClose
@closeChip="() => setValue(componentId, null)"
/>
<StudioButton
v-if="Object.keys(getFilteredValues(selectedValues)).length"
variant="text"
trackingId="registrants-clear-all-filters-button"
@click="clearValues"
>
Clear All Filters
</StudioButton>
</div>
</template>
</selected-filters>
<div class="content tw-w-full tw-mb-8 tw-relative">
<div class="tw-absolute tw-right-[168px] tw-top-[12px]">
<StudioRegistrantsColumnSelectionPopup v-if="$store.state.registration.lastSearchedQueryResultSize > 0" />
</div>
<reactive-list
ref="datalist"
dataField="email"
componentId="SearchResults"
prevLabel="Previous"
nextLabel="Next"
:react="{ and: oredFilters }"
:pagination="true"
:defaultQuery="defaultQuery"
:sortOptions="sortOptions"
:size="25"
:showEndPage="true"
:showResultStats="false"
:innerClass="{
sortOptions: 'appbase-sort-options',
pagination: 'appbase-paginator'
}"
:renderNoResults="() => null"
@data="onResult"
@queryChange="onQueryChange"
>
<template #render="{ data }">
<div>
<div class="tw-flex tw-flex-row tw-flex-1 tw-justify-between tw-py-3">
<StudioRegistrantsActions
:react="{ and: oredFilters }"
:defaultQuery="defaultQuery"
@banfinished="handleBanFinished"
/>
</div>
<v-data-table
:headers="visibleColumns"
:items="data"
showSelect
fixedHeader
class="tw-border tw-border-slate-200 tw-overflow-hidden"
>
<!--
Do not bind props to this component. Route them through the store.
If you bind props to this element other than items then everytime
this component changes the reactive-list component above switches
back to page 1. If you route the changes through the store it cannot
tell.
-->
<template #headers="{ columns }">
<!-- Do not use v-if here. It will break filters. -->
<StudioRegistrantsTableHeader v-show="data.length > 0" :headers="columns" />
</template>
<template #bottom></template>
<template #body></template>
<template #tbody="{ items }">
<div v-if="items.length === 0" class="tw-py-12 tw-text-center">
<h5
class="tw-font-medium tw-leading-tight tw-text-md lg:tw-text-lg lg:tw-leading-tight tw-text-slate-400 dark:tw-text-slate-500"
>
No registrants yet!
</h5>
</div>
<StudioRegistrantsTableRow
:rows="items"
@singleItemSelectionHandler="singleItemSelectionHandler"
/>
</template>
</v-data-table>
</div>
</template>
</reactive-list>
</div>
</div>
</reactive-base>
</div>
</div>
</template>
<script>
import { mapState, mapActions, mapGetters, mapMutations } from 'vuex';
import StudioRegistrantsTableRow from './StudioRegistrantsTableRow.vue';
import StudioRegistrantsTableHeader from './StudioRegistrantsTableHeader.vue';
import StudioRegistrantsActions from './StudioRegistrantsActions.vue';
import StudioRegistrantsColumnSelectionPopup from './StudioRegistrantsColumnSelectionPopup.vue';
import StudioAddRegistrant from './StudioAddRegistrant.vue';
import { snakeCaseToCapitalized } from '@/store/modules/registration';
import LogRocket from 'logrocket';
import StudioButton from '@/studioComponents/StudioButton.vue';
import StudioPageIntro from '@/studioComponents/StudioPageIntro.vue';
import StudioSpinner from '@/studioComponents/StudioSpinner.vue';
import Chip from '@/commonComponents/Chip.vue';
import throttle from 'lodash.throttle';
import Refresh from '@/studioComponents/Icons/Refresh.vue';
import { VDataTable } from 'vuetify/labs/VDataTable';
export default {
name: 'StudioRegistrantsWrapper',
components: {
StudioRegistrantsTableHeader,
StudioRegistrantsTableRow,
StudioRegistrantsActions,
StudioRegistrantsColumnSelectionPopup,
StudioAddRegistrant,
Chip,
StudioButton,
StudioSpinner,
StudioPageIntro,
Refresh,
VDataTable
},
computed: {
...mapState('registration', {
appBaseUrl: 'appBaseUrl',
loadingRegistration: 'loadingRegistration',
appbaseError: 'appbaseError',
columns: 'columns',
roleNames: 'roleNames',
lastSearchedQueryResultSize: 'lastSearchedQueryResultSize'
}),
...mapGetters('registration', ['visibleColumns']),
...mapGetters('studioEventList', {
event: 'getCurrentEvent'
}),
appbaseCredentials() {
return this.event?.appbase_credentials;
},
selectedFilterValue() {
return (componentId, selectedValues) => {
if (componentId === 'role') {
return selectedValues[componentId].value
.map(role => {
return this.roleNames[role];
})
.join(',');
}
const value = selectedValues[componentId].value;
if (Array.isArray(value)) {
return value.join(',');
}
return value;
};
},
oredFilters() {
const ored = this.columns
.map(it => it.value)
.filter(it => {
return !['user__magic_link', 'id', 'event_id', 'profile_picture_url'].includes(it);
});
return ored.concat('SearchUsers').concat('role');
},
eventId() {
return this.$route.params.eventId;
},
summary() {
if (this.loadingRegistration) {
return '';
}
if (this.lastSearchedQueryResultSize <= 0) {
return '';
}
if (this.lastSearchedQueryResultSize === 1) {
return '1 Registrant';
}
return `${this.lastSearchedQueryResultSize} Registrants`;
}
},
data() {
return {
loadingColumns: true,
currentPage: 1,
sortOptions: [
{
label: 'Full Name ↑',
dataField: 'full_name.keyword',
sortBy: 'asc'
},
{
label: 'Full Name ↓',
dataField: 'full_name.keyword',
sortBy: 'desc'
},
{
label: 'Email ↑',
dataField: 'email.keyword',
sortBy: 'asc'
},
{
label: 'Email ↓',
dataField: 'email.keyword',
sortBy: 'desc'
},
{
label: 'Date Registered ↑',
dataField: 'date_registered.keyword',
sortBy: 'asc'
},
{
label: 'Date Registered ↓',
dataField: 'date_registered.keyword',
sortBy: 'desc'
}
]
};
},
mounted() {
this.fetchColumns().then(() => {
this.loadingColumns = false;
});
this.fetchEventMembers({ eventId: this.eventId });
},
beforeRouteLeave(to, from, next) {
this.toggleSelectAll(false);
this.toggleColumnPopup(false);
this.setSelectedQuery(null);
next();
},
methods: {
...mapActions('registration', ['fetchColumns']),
...mapActions('eventList', ['getEventMembers', 'blockEventBulkUser']),
...mapActions('studioEventList', ['fetchEventMembers']),
...mapMutations('registration', [
'toggleSelectAll',
'toggleColumnPopup',
'setSelectedQuery',
'setLoadingRegistration',
'storeAppbaseQueryDSL',
'toggleSelection'
]),
// This cannot be a computed property because it is inside the render
// function of appbase and hence cannot know when to recompute unless
// its passed as a method
handleBanFinished({ timeout }) {
this.toggleSelectAll(false);
this.searchRegistrants();
// The banned data doesn't reflect in appbase immediately so wait to refresh
setTimeout(() => {
this.refreshList();
}, timeout);
},
refreshList: throttle(function () {
const datalist = this.$refs.datalist;
if (datalist) {
this.setLoadingRegistration(true);
if (!this.columns?.length) {
this.fetchColumns();
}
const currentPage = datalist.$el.__vue__.currentPageState;
datalist.$el.__vue__.setPage(currentPage + 1);
setTimeout(() => {
datalist.$el.__vue__.setPage(currentPage);
this.setLoadingRegistration(false);
}, 100);
} else {
LogRocket.info('Could not refresh by page change because reactivebase was not found!');
}
}, 2000),
filteredText(componentId) {
return this.snakeCaseToCapitalized(componentId === 'blocked' ? 'banned' : componentId);
},
onQueryChange(prev, next) {
LogRocket.info('Query change: ', prev, next);
this.storeAppbaseQueryDSL({
next,
numResults: -1
});
},
onResult(payload) {
const numResults = payload.resultStats.numberOfResults;
LogRocket.info('Number of results in appbase query: ' + numResults);
this.storeAppbaseQueryDSL({
numResults
});
},
searchRegistrants() {
if (this.currentPage == 1) {
this.fetchEventMembers({ eventId: this.event.id });
} else {
this.currentPage = 1;
}
},
snakeCaseToCapitalized,
getFilteredValues(values = {}) {
const filteredValues = {};
Object.keys(values).forEach(componentId => {
if (
values[componentId].showFilter &&
(Array.isArray(values[componentId].value) ? values[componentId].value.length : !!values[componentId].value)
) {
filteredValues[componentId] = values[componentId];
}
});
return filteredValues;
},
defaultQuery() {
return {
track_total_hits: true,
query: { match_all: {} }
};
},
singleItemSelectionHandler(row) {
this.toggleSelection(row);
}
}
};
</script>
<style lang="scss">
// This block is for modifying appbase css
// Do not scope it
.appbase-search {
& #SearchUsers-input {
font-size: 14px;
min-height: 0;
border-radius: 999px;
border-color: rgb(209 213 219);
background-color: white;
&:focus {
border-color: rgb(135 128 253);
}
}
& svg.search-icon[alt='Search'] {
fill: #222;
height: 12px;
width: 12px;
}
}
.search-bar-list {
border: 1px solid rgb(209 213 219) !important;
max-height: 160px !important;
margin-top: 2px !important;
border-radius: 0.25rem !important;
}
select.appbase-sort-options {
position: absolute;
right: 2px;
top: 8px;
width: 160px;
border-radius: 0.25rem;
cursor: pointer;
&:hover {
border-color: rgb(135 128 253);
outline-color: rgb(135 128 253);
}
&:focus {
border-color: rgb(135 128 253);
outline: 2px solid rgb(135 128 253);
outline-offset: 0px;
}
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
background-position: right 0.5rem center;
background-repeat: no-repeat;
background-size: 1.5em 1.5em;
padding-right: 2.5rem;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
.content {
& .appbase-paginator {
text-align: center;
& > button {
font-family: 'Inter var', sans-serif;
&.active {
background-color: rgb(135 128 253);
color: white;
}
}
& > a {
color: rgba(28, 30, 31, 0.6);
background-color: transparent;
&.active {
color: white;
background: rgba(28, 30, 31, 0.6);
}
&:focus {
outline: none;
}
}
}
& .appbase-noresults {
display: none;
}
}
</style>
- Langage dominant
- JavaScript
- Étoiles
- 4.9k
- Forks
- 479
- Merge moyen
- 4 j 10 h
- PR mergées (30 j)
- 4
Préparer son environnement
- Aucun Dockerfile ni fichier Docker Compose
- Propose un modèle de pull request
- Lire le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Autres issues de appbaseio/reactivesearch
-
ci: add Node.js version matrix, caching, and lint step to test workflowPeut-être pris @Talha12Shiekh l’a pris il y a 71 jours. Ouverte
Difficulté 2/5 1-3 heures Accessibilité débutants 84/100
appbaseio/reactivesearch#2322 ·
Les mainteneurs répondent en général sous 1 jour
-
chore: add Dependabot config for automated dependency updatesPeut-être pris @Talha12Shiekh l’a pris il y a 70 jours. Ouverte
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
appbaseio/reactivesearch#2321 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
security: user-controlled URL params injected directly into Redux store via JSON.parsePeut-être pris @Talha12Shiekh l’a pris il y a 70 jours. Ouverte
Difficulté 2/5 1-3 heures Accessibilité débutants 68/100
appbaseio/reactivesearch#2319 ·
Les mainteneurs répondent en général sous 1 jour
-
refactor: consolidate 12 near-identical SVG icon components into one patternPeut-être pris @Talha12Shiekh l’a pris il y a 72 jours. Ouverte
Difficulté 4/5 3-5 jours Accessibilité débutants 48/100
appbaseio/reactivesearch#2324 ·
Les mainteneurs répondent en général sous 1 jour
-
refactor: SearchBox.js is 2692 lines — extract subcomponents for maintainabilityPeut-être pris @Talha12Shiekh l’a pris il y a 71 jours. Ouverte
Difficulté 4/5 3-5 jours Accessibilité débutants 55/100
appbaseio/reactivesearch#2323 ·
Les mainteneurs répondent en général sous 1 jour
Toutes les issues de appbaseio/reactivesearch
Issues similaires
-
Difficulté 2/5 1-3 heures Accessibilité débutants 65/100
NaturalIntelligence/fast-xml-parser#888 · 1 commentaire ·
Les mainteneurs répondent en général sous 2 jours
-
bug callouts regression revealjs
Difficulté 2/5 1-3 heures Accessibilité débutants 72/100
quarto-dev/quarto-cli#15014 ·
Les mainteneurs répondent en général sous 1 jour
-
Remove: Fox Deportes SDOuvertecheck:passed feeds:remove
Difficulté 1/5 Moins d'une heure Accessibilité débutants 65/100
iptv-org/database#37176 · 1 commentaire · 1 réaction ·
Les mainteneurs répondent en général sous 9 jours
-
Difficulté 2/5 1-3 heures Accessibilité débutants 72/100
hawk-digital-environments/HAWKI#443 ·
Les mainteneurs répondent en général sous 1 jour
-
documentation v2
Difficulté 2/5 1-3 heures Accessibilité débutants 82/100
modelcontextprotocol/python-sdk#3662 ·
Les mainteneurs répondent en général sous 1 jour