Skip to content
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
16 changes: 16 additions & 0 deletions src/adapter/search-request-adapter/search-params-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,22 @@ export function adaptSearchParams(
'*',
]

// Highlight pre tag
const highlightPreTag = searchContext?.highlightPreTag
if (highlightPreTag) {
meiliSearchParams.highlightPreTag = highlightPreTag
} else {
meiliSearchParams.highlightPreTag = '__ais-highlight__'
}

// Highlight post tag
const highlightPostTag = searchContext?.highlightPostTag
if (highlightPostTag) {
meiliSearchParams.highlightPostTag = highlightPostTag
} else {
meiliSearchParams.highlightPostTag = '__/ais-highlight__'
}

const placeholderSearch = searchContext.placeholderSearch
const query = searchContext.query

Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,70 @@
import { adaptHighlight } from './highlight-adapter'
import { SearchContext } from '../../../types'
import { isPureObject } from '../../../utils'

/**
* Adapt Meilisearch formating to formating compliant with instantsearch.js.
* Stringify values following instantsearch practices.
*
* @param {any} value - value that needs to be stringified
*/
function stringifyValue(value: any) {
if (typeof value === 'string') {
// String
return value
} else if (value === undefined) {
// undefined
return JSON.stringify(null)
} else {
return JSON.stringify(value)
}
}

/**
* Recursif function wrap the deepest possible value
* the following way: { value: "xx" }.
*
* For example:
*
* {
* "rootField": { "value": "x" }
* "nestedField": { child: { value: "y" } }
* }
*
* recursivity continues until the value is not an array or an object.
*
* @param {any} value - value of a field
*
* @returns Record<string, any>
*/
function wrapValue(value: any): Record<string, any> {
if (Array.isArray(value)) {
// Array
return value.map((elem) => wrapValue(elem))
} else if (isPureObject(value)) {
// Object
return Object.keys(value).reduce<Record<string, any>>(
(nested: Record<string, any>, key: string) => {
nested[key] = wrapValue(value[key])

return nested
},
{}
)
} else {
return { value: stringifyValue(value) }
}
}

/**
* Adapt Meilisearch formatted fields to a format compliant to instantsearch.js.
*
* @param {Record<string} formattedHit
* @param {SearchContext} searchContext
* @returns {Record}
*/
export function adaptFormating(
hit: Record<string, any>,
searchContext: SearchContext
export function adaptFormattedFields(
hit: Record<string, any>
): Record<string, any> {
const preTag = searchContext?.highlightPreTag
const postTag = searchContext?.highlightPostTag

if (!hit._formatted) return {}
const _formattedResult = adaptHighlight(hit, preTag, postTag)
if (!hit) return {}
const _formattedResult = wrapValue(hit)

const highlightedHit = {
// We could not determine what the differences are between those two fields.
Expand Down

This file was deleted.

22 changes: 13 additions & 9 deletions src/adapter/search-response-adapter/hits-adapter.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { PaginationContext, SearchContext } from '../../types'
import { adaptPagination } from './pagination-adapter'
import { adaptFormating } from './format-adapter'
import { adaptFormattedFields } from './format-adapter'
import { adaptGeoResponse } from './geo-reponse-adapter'

/**
Expand All @@ -18,19 +18,23 @@ export function adaptHits(
const { hitsPerPage, page } = paginationContext
const paginatedHits = adaptPagination(hits, page, hitsPerPage)

let formattedHits = paginatedHits.map((hit: Record<string, any>) => {
let adaptedHits = paginatedHits.map((hit: Record<string, any>) => {
// Creates Hit object compliant with InstantSearch
if (Object.keys(hit).length > 0) {
const { _formatted: formattedHit, _matchesInfo, ...restOfHit } = hit
const { _formatted: formattedHit, _matchesInfo, ...documentFields } = hit

return {
...restOfHit,
...adaptFormating(hit, searchContext),
...(primaryKey && { objectID: hit[primaryKey] }),
const adaptedHit: Record<string, any> = Object.assign(
documentFields,
adaptFormattedFields(formattedHit)
)

if (primaryKey) {
adaptedHit.objectID = hit[primaryKey]
}
return adaptedHit
}
return hit
})
formattedHits = adaptGeoResponse(formattedHits)
return formattedHits
adaptedHits = adaptGeoResponse(adaptedHits)
return adaptedHits
}
1 change: 1 addition & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './array'
export * from './string'
export * from './number'
export * from './object'
3 changes: 3 additions & 0 deletions src/utils/object.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function isPureObject(data: any) {
return typeof data === 'object' && !Array.isArray(data) && data !== null
}
4 changes: 2 additions & 2 deletions tests/assets/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,8 @@ export type Movies = {
poster?: string
genres?: string[]
release_date?: number // eslint-disable-line
undefinedArray?: [undefined, undefined, undefined]
nullArray?: [null]
undefinedArray?: undefined[]
nullArray?: null[]
objectArray?: Array<{ name: string }>
object?: {
id?: number
Expand Down
19 changes: 12 additions & 7 deletions tests/highlight.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,13 +231,18 @@ describe('Highlight Browser test', () => {
{
indexName: 'movies',
params: {
query: 'Ariel',
query: 'hello',
attributesToHighlight: ['*'],
},
},
])

const hit = response.results[0].hits[0]._highlightResult

if (hit?.title) {
expect(hit?.title?.value).toEqual('Ariel')
}

Comment on lines +242 to +245
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand how you get this result with your query?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is from inside the nested object!

expect(hit?.objectArray[0]?.name.value).toEqual(
        '__ais-highlight__hello__/ais-highlight__ world'
      )

if (hit?.genres) {
expect(hit?.genres[0]?.value).toEqual('Drama')
expect(hit?.genres[1]?.value).toEqual('Crime')
Expand All @@ -260,15 +265,15 @@ describe('Highlight Browser test', () => {
}

if (hit?.objectArray) {
// @ts-ignore
expect(hit?.objectArray[0]?.value).toEqual('{"name":"hello world"}')
// @ts-ignore
expect(hit?.objectArray[1]?.value).toEqual('{"name":"hello world"}')
expect(hit?.objectArray[0]?.name.value).toEqual(
'__ais-highlight__hello__/ais-highlight__ world'
)
}

if (hit?.object) {
// @ts-ignore
expect(hit?.object?.value).toEqual('{"id":"1","name":"One two"}')
expect(hit?.object?.id?.value).toEqual('1')

expect(hit?.object?.name?.value).toEqual('One two')
}

if (hit?.nullField) {
Expand Down
20 changes: 8 additions & 12 deletions tests/snippets.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -297,15 +297,13 @@ describe('Snippet Browser test', () => {
}

if (hit?.objectArray) {
// @ts-ignore
expect(hit?.objectArray[0]?.value).toEqual('{"name":"hello…"}')
// @ts-ignore
expect(hit?.objectArray[1]?.value).toEqual('{"name":"hello…"}')
expect(hit?.objectArray[0]?.name?.value).toEqual('hello…')
expect(hit?.objectArray[1]?.name?.value).toEqual('hello…')
}

if (hit?.object) {
// @ts-ignore
expect(hit?.object?.value).toEqual('{"id":"1","name":"One…"}')
expect(hit?.object?.name?.value).toEqual('One…')
expect(hit?.object?.id?.value).toEqual('1')
}

if (hit?.nullField) {
Expand Down Expand Up @@ -357,15 +355,13 @@ describe('Snippet Browser test', () => {
}

if (hit?.objectArray) {
// @ts-ignore
expect(hit?.objectArray[0]?.value).toEqual('{"name":"hello( •_•)"}')
// @ts-ignore
expect(hit?.objectArray[1]?.value).toEqual('{"name":"hello( •_•)"}')
expect(hit?.objectArray[0]?.name?.value).toEqual('hello( •_•)')
expect(hit?.objectArray[1]?.name?.value).toEqual('hello( •_•)')
}

if (hit?.object) {
// @ts-ignore
expect(hit?.object?.value).toEqual('{"id":"1","name":"One( •_•)"}')
expect(hit?.object?.id?.value).toEqual('1')
expect(hit?.object?.name?.value).toEqual('One( •_•)')
}

if (hit?.nullField) {
Expand Down