meilisearch / meilisearch-js-plugins

The search client to use Meilisearch with InstantSearch.
https://www.meilisearch.com
MIT License
469 stars 57 forks source link

Formating ais-highlight attribute with markdown content #597

Open adhamfarrag opened 2 years ago

adhamfarrag commented 2 years ago

Description Using Strapi, strapi-meilisearch-plugin and instant-meilisearch here.

Trying to render results from a search bar, typical article model (title, body, etc). And as strapi uses markdown, results come back with markdown syntanx. Tried to modify ais-highlight to render article body using markdown-it.

Is there anyway around my problem ?

 <ais-highlight
        attribute="body"
        :hit="hit"
        class="block text-xs font-medium  text-primary line-clamp-3"
   >

 <ais-highlight
        :attribute="$md.render(body)"
        :hit="hit"
        class="block text-xs font-medium  text-primary line-clamp-3"
      >

Expected behavior I expected it to allow me to render readable markdown content.

Current behavior I get Input data should be a String error.

Screenshots or Logs

Screen Shot 2021-11-12 at 12 47 37 AM

Environment (please complete the following information):

bidoubiwa commented 2 years ago

Hey @adhamfarrag Could you show me one of the documents in MeiliSearch? So I can try it out and debug!

adhamfarrag commented 2 years ago

Hey @bidoubiwa Thanks for checking this out.

{
  "id": 2,
  "title": "Demo Article in second section",
  "slug": "demo-article-in-second-section",
  "body": "Yes. New [link](https://www.google.com/) Culpa aperiam accusamus consectetur rerum. Omnis esse officia aperiam reiciendis laudantium. Iste dolor nulla incidunt. Atque tempore ipsum itaque. Earum molestiae impedit nobis ex itaque accusamus. Maxime cum quae. Ut repellendus modi in officia. Aut tempora error iusto et maiores id blanditiis voluptatem. Eum et tenetur autem fugiat repudiandae sed nihil illo. Eveniet alias et qui omnis odio pariatur voluptatem corrupti ratione. Dolores nisi libero. Alias et tempore maiores odio. Illum sed odit corporis enim dolore laudantium non.",
  "Author": {
    "id": 4,
    "username": "adhamfarrag",
    "email": "adham@platform.com",
    "provider": "local",
    "confirmed": true,
    "blocked": false,
    "role": 1,
    "created_at": "2021-10-26T14:25:35.086Z",
    "updated_at": "2021-11-04T14:39:33.937Z",
    "Rank": "Owner",
    "firstName": "Adham",
    "lastName": "Farrag"
  },
  "section": {
    "id": 2,
    "title": "X second section",
    "slug": "x-second-section",
    "published_at": "2021-10-27T19:29:29.032Z",
    "created_at": "2021-10-27T19:29:02.724Z",
    "updated_at": "2021-11-02T18:32:21.336Z",
    "organization": 4,
    "description": null
  },
  "published_at": "2021-10-27T19:29:24.345Z",
  "created_at": "2021-10-27T16:07:10.382Z",
  "updated_at": "2021-11-01T18:51:02.503Z",
  "Attachments": []
}

It's and index for articles and this is a single article. Is this is what you're asking for ?

bidoubiwa commented 2 years ago

Yes thanks you :) Could you also provide your whole instant-search component?

adhamfarrag commented 2 years ago

Here you are :)


<template>
  <div>
    <ais-instant-search
      :index-name="selectedIndex"
      :search-client="searchClient"
    >
      <ais-configure
        :attributesToSnippet="['bodyPlainText']"
        :htos-per-page.camel="5"
      >
        <ais-autocomplete>
          <div slot-scope="{ currentRefinement, indices, refine }">
            <div class="group">
              <div
                class="relative flex items-center w-full h-12 duration-200 bg-white border-2 rounded-lg  group-focus-within:border-accent border-shade"
              >
                <IconSearch
                  class="absolute left-0 w-5 h-5 ml-5 text-gray-400 fill-current  group-focus-within:text-accent"
                />
                <input
                  type="search"
                  ref="searchInput"
                  class="w-full ml-12 mr-5 outline-none  ring-0 focus:border-accent border-shade focus:outline-none focus:ring-0"
                  placeholder="Search"
                  :value="currentRefinement"
                  @input="refine($event.currentTarget.value)"
                  autocomplete="off"
                />

                <div class="absolute right-0 flex flex-row">
                  <span
                    v-if="!currentRefinement"
                    class="py-1 pr-5 text-gray-400  group-focus-within:text-accent md:inline-block"
                  >
                    /
                  </span>
                </div>
              </div>
            </div>

            <div
              v-show="currentRefinement.length"
              class="absolute z-10 w-screen max-w-lg px-2 mt-3 transform  sm:px-0"
            >
              <div
                class="overflow-hidden rounded-lg shadow-lg  ring-1 ring-black ring-opacity-5"
              >
                <div
                  v-if="currentRefinement"
                  class="relative flex flex-col px-5 py-6 bg-white  text-primary sm:gap-8 sm:p-8"
                >
                  <div
                    v-for="section in indices"
                    :key="section.ObjectID"
                    class="divide-y divide-accent"
                  >
                    <nuxt-link
                      :to="
                        selectedIndex == 'article'
                          ? `/organization/${$strapi.user.organizations[0].slug}/section/${hit.section.slug}/article/${hit.slug}`
                          : `/organization/${$strapi.user.organizations[0].slug}/dailyUpdate/${hit.slug}/`
                      "
                      v-for="(hit, index) in section.hits"
                      :key="index"
                      class="py-2 text-sm transition ease-in-out duration 200"
                    >
                      <div
                        v-if="
                          hit.section.organization ==
                          $strapi.user.organizations[0].id
                        "
                        class="w-full px-2 py-3"
                      >
                        <ais-highlight
                          attribute="title"
                          :hit="hit"
                          class="block text-lg font-medium text-accent"
                        />

                        <ais-highlight
                          attribute="body"
                          :hit="hit"
                          class="block text-xs line-clamp-2 text-primary"
                        />
                      </div>
                    </nuxt-link>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </ais-autocomplete>
      </ais-configure>
    </ais-instant-search>

    <div class="flex flex-row mt-4 space-x-2">
      <div v-for="index in indexes" :key="index.id">
        <span
          @click="updateIndex(index.indexName)"
          :class="
            selectedIndex == index.indexName
              ? ' bg-accent text-white border-accent '
              : 'border-shade text-primary  '
          "
          class="
            px-2
            py-1.5
            text-xs
            duration-200
            transform
            border-2
            rounded-lg
            cursor-pointer
          "
        >
          {{ index.label }}
        </span>
      </div>
    </div>
  </div>
</template>

<script>
import { instantMeiliSearch } from '@meilisearch/instant-meilisearch'

export default {
  data() {
    return {
      searchClient: instantMeiliSearch(
        'url',
        'key'
      ),
      selectedIndex: 'article',
      indexes: [
        {
          label: 'Articles',
          indexName: 'article',
        },
        {
          label: 'Daily Updates',
          indexName: 'daily-update',
        },
      ],
    }
  },
  methods: {
    searchFocusListener(event) {
      if (event.keyCode === 47 && !(event.target instanceof HTMLInputElement)) {
        event.preventDefault()
        this.focusSearchInput()
      }
    },
    focusSearchInput() {
      this.$refs.searchInput.focus()
    },
    updateIndex(index) {
      this.selectedIndex = index
    },
  },
  beforeMount() {
    window.addEventListener('keypress', this.searchFocusListener)
  },
  beforeDestroy() {
    window.removeEventListener('keypress', this.searchFocusListener)
  },
}
</script>
adhamfarrag commented 2 years ago

Hi @bidoubiwa! I tried using marked.js to generate plain text. Worked inside terminal but gave me the same error with ais-highlight component. Tried using span tag and it worked. I guess the problem is with binding attribute and strict checking of content type.

const marked = require('marked').marked
const PlainTextRenderer = require('marked-plaintext')
const renderer = new PlainTextRenderer()

function convertToPlainText(content) {
  return marked(content, { renderer: renderer })
}

export { convertToPlainText }
bidoubiwa commented 2 years ago

Hey @adhamfarrag I will be looking into your bug as soon as I have time. We are very busy for the moment. I'll get back to you asap