Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

Changes in appbase query after migrating to 3.1.0-alpha.1

オープン
#2,276 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

まだ誰も着手していません。

評価

難易度
4/5
見積もり時間
3〜5日
初心者へのやさしさ
25/100
issue の種類
バグ
明瞭さ
説明が足りない
活発さ
停滞
技術スタック
javascript
領域
frontend, search

調査の方向性

示されている reactive-list のエントリーポイントから始め、特に @queryChange、defaultQuery、react 設定を確認し、2つのライブラリバージョン間で生成されるリクエストを比較します。提供された Vue コンポーネントとスクリーンショットを使って一括更新の失敗を再現します。完了条件は、query の形状の変更と、リクエストを維持または適応するサポートされた方法を明らかにすることです。

索引モデルが issue の本文から書いたものです。

説明

bug

Affected Projects
Vue.JS

Library Version: x.y.z
"@appbaseio/reactivesearch-vue": "3.1.0-alpha.1"

Describe the bug
Lately, our application transitioned from Vue 2 to Vue 3, prompting an upgrade in the Appbase version from ^1.16.0-alpha.31 to 3.1.0-alpha.1. For performing bulk updates, we send queries to our backend, which subsequently calls the Appbase API to modify user data. However, due to the upgraded Appbase version, we've noticed alterations in the queries generated by Appbase. Consequently, these changes are causing failures in our backend operations.

The query displayed on the left side pertains to the older version, whereas the one on the right side corresponds to the new version, as shown in the image below.

Screenshot 2023-12-19 at 7 28 14 PM

Is there a way to utilize or retrieve the old query within the new app version itself? Alternatively, what could be another straightforward solution to implement, considering our release is currently stalled due to this issue?

Screenshots
Screenshot 2023-12-19 at 7 31 58 PM
Screenshot 2023-12-19 at 7 32 05 PM

Desktop (please complete the following information):

  • Browser [e.g. chrome]

@queryChange is sending us the query

<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 v-if="show" class="tw-flex tw-w-full tw-flex-1 tw-flex-col tw-overflow-scroll">
      <!--
        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-mt-1 tw-flex tw-flex-1 tw-flex-row tw-items-center tw-justify-start">
              <StudioButton
                variant="outline"
                trackingId="registrants-refresh-button"
                class="tw-ml-0.5 tw-mr-2"
                @click="refreshList"
              >
                <template #icon>
                  <TablerRefresh />
                </template>
              </StudioButton>
              <div v-if="summary" class="tw-mr-2">
                {{ summary }}
              </div>
            </div>

            <div class="tw-flex tw-flex-row tw-items-start tw-justify-end tw-pl-2 tw-pr-1">
              <SearchBox
                placeholder="Search Registrants..."
                className="appbase-search"
                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-relative tw-mb-8 tw-w-full">
            <div class="tw-absolute tw-right-[168px] tw-top-[12px]">
              <StudioRegistrantsColumnSelectionPopup v-if="$store.state.registration.lastSearchedQueryResultSize > 0" />
            </div>
            <reactive-list
              componentId="SearchResults"
              prevLabel="Previous"
              :sortOptions="sortOptions"
              nextLabel="Next"
              :react="{ and: oredFilters }"
              :pagination="true"
              :defaultQuery="defaultQuery"
              :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-1 tw-flex-row tw-justify-between tw-py-3">
                    <StudioRegistrantsActions
                      :react="{ and: oredFilters }"
                      :defaultQuery="defaultQuery"
                      @banfinished="handleBanFinished"
                    />
                  </div>
                  <v-data-table
                    :headers="visibleColumns"
                    :items="data"
                    showSelect
                    :itemsPerPage="25"
                    fixedHeader
                    class="tw-overflow-hidden tw-border tw-border-slate-200"
                  >
                    <!--
                      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-text-md tw-font-medium tw-leading-tight tw-text-slate-400 dark:tw-text-slate-500 lg:tw-text-lg lg:tw-leading-tight"
                        >
                          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 logger from '@goldcast/logger';
import throttle from 'lodash.throttle';
import TablerRefresh from '@/studioComponents/Icons/tabler/TablerRefresh.vue';
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 { VDataTable } from 'vuetify/labs/VDataTable';

export default {
  name: 'StudioRegistrantsWrapper',
  components: {
    StudioRegistrantsTableHeader,
    StudioRegistrantsTableRow,
    StudioRegistrantsActions,
    StudioRegistrantsColumnSelectionPopup,
    StudioAddRegistrant,
    Chip,
    StudioButton,
    StudioSpinner,
    StudioPageIntro,
    TablerRefresh,
    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,
      show: false,
      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.show = true;
    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('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 () {
      this.show = false;

      if (!this.columns?.length) {
        this.fetchColumns();
      }

      setTimeout(() => {
        this.show = true;
      }, 100);
    }, 2000),
    filteredText(componentId) {
      return this.snakeCaseToCapitalized(componentId === 'blocked' ? 'banned' : componentId);
    },
    onQueryChange(prev, next) {
      logger.info('Query change: ', prev, next);
      this.storeAppbaseQueryDSL({
        next,
        numResults: -1
      });
    },
    onResult(payload) {
      const numResults = payload.resultStats.numberOfResults;
      logger.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>

@siddharthlatest @SavvyShah can you guys please help

主要言語
JavaScript
スター
4.9k
フォーク
479
平均マージ
4時間 43分
マージ済み PR(30日)
3

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

appbaseio/reactivesearch のほかの issue

appbaseio/reactivesearch の issue をすべて見る

似ている issue

JavaScript の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。