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
2 changes: 1 addition & 1 deletion .github/workflows/windows-signer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
cd ${{ env.ARCHIVE_DIR }}/out
signtool sign /tr http://timestamp.globalsign.com/tsa/r6advanced1 /td sha256 /fd sha256 /a ${{ inputs.files }}
- name: collect signed file diff
shell: powershell
shell: powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File {0}
run: |
$OutDir = Join-Path '${{ env.ARCHIVE_DIR }}' 'out'
$DiffDir = Join-Path '${{ env.ARCHIVE_DIR }}' 'signed-diff'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { isEmpty, omit } from 'lodash-es';
import { map, type Observable, of, switchMap } from 'rxjs';
import { z } from 'zod';

import { normalizeSearchText } from '../../../utils/normalize-search-text';
import type { DocsService } from '../../doc/services/docs';
import type { WorkspaceService } from '../../workspace';

Expand All @@ -26,24 +27,6 @@ export class DocsSearchService extends Service {
errorMessage: null,
} as IndexerSyncState);

private normalizeHighlight(value?: string | null) {
if (!value) {
return value ?? '';
}
try {
const parsed = JSON.parse(value);
if (Array.isArray(parsed)) {
return parsed.join(' ');
}
if (typeof parsed === 'string') {
return parsed;
}
} catch {
// ignore parse errors, return raw value
}
return value;
}

searchTitle$(query: string) {
return this.indexer
.search$(
Expand Down Expand Up @@ -144,12 +127,12 @@ export class DocsSearchService extends Service {
const firstMatchFlavour = bucket.hits.nodes[0]?.fields.flavour;
if (firstMatchFlavour === 'affine:page') {
// is title match
const blockContent = this.normalizeHighlight(
const blockContent = normalizeSearchText(
bucket.hits.nodes[1]?.highlights.content[0]
); // try to get block content
result.push({
docId: bucket.key,
title: this.normalizeHighlight(
title: normalizeSearchText(
bucket.hits.nodes[0].highlights.content[0]
),
score: bucket.score,
Expand All @@ -169,7 +152,7 @@ export class DocsSearchService extends Service {
? matchedBlockId
: matchedBlockId[0],
score: bucket.score,
blockContent: this.normalizeHighlight(
blockContent: normalizeSearchText(
bucket.hits.nodes[0]?.highlights.content[0]
),
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { html } from 'lit';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
import { catchError, map, of } from 'rxjs';

import { normalizeSearchText } from '../../../utils/normalize-search-text';
import type { CollectionMeta, CollectionService } from '../../collection';
import type { DocDisplayMetaService } from '../../doc-display-meta';
import type { DocsSearchService } from '../../docs-search';
Expand Down Expand Up @@ -185,14 +186,16 @@ export class SearchMenuService extends Service {
typeof node.fields.docId === 'string'
? node.fields.docId
: node.fields.docId[0];
const title =
const title = normalizeSearchText(
typeof node.fields.title === 'string'
? node.fields.title
: node.fields.title[0];
: node.fields.title[0]
);
const highlights = normalizeSearchText(node.highlights?.title?.[0]);
return {
id,
title,
highlights: node.highlights?.title?.[0],
highlights: highlights || undefined,
};
})
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { expect, test } from 'vitest';

import { normalizeSearchText } from '../normalize-search-text';

test('normalizeSearchText should keep plain text unchanged', () => {
expect(normalizeSearchText('hello world')).toBe('hello world');
});

test('normalizeSearchText should decode serialized single-item array', () => {
expect(normalizeSearchText('["hello world"]')).toBe('hello world');
});

test('normalizeSearchText should decode serialized highlighted array', () => {
expect(normalizeSearchText('["<b>hello</b> world"]')).toBe(
'<b>hello</b> world'
);
});

test('normalizeSearchText should join serialized multi-item array', () => {
expect(normalizeSearchText('["hello","world"]')).toBe('hello world');
});

test('normalizeSearchText should decode serialized string', () => {
expect(normalizeSearchText('"hello world"')).toBe('hello world');
});

test('normalizeSearchText should keep invalid serialized text unchanged', () => {
expect(normalizeSearchText('["hello"')).toBe('["hello"');
});

test('normalizeSearchText should support array input', () => {
expect(normalizeSearchText(['hello', 'world'])).toBe('hello world');
});
1 change: 1 addition & 0 deletions packages/frontend/core/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * from './channel';
export * from './create-emotion-cache';
export * from './event';
export * from './extract-emoji-icon';
export * from './normalize-search-text';
export * from './string2color';
export * from './toast';
export * from './unflatten-object';
62 changes: 62 additions & 0 deletions packages/frontend/core/src/utils/normalize-search-text.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
interface NormalizeSearchTextOptions {
fallback?: string;
arrayJoiner?: string;
}

function tryParseSerializedText(value: string): unknown | null {
const trimmed = value.trim();
if (!trimmed) {
return null;
}

// Indexer/storage may serialize text arrays as JSON strings like ["foo"].
if (!trimmed.startsWith('[') && !trimmed.startsWith('"')) {
return null;
}

try {
return JSON.parse(trimmed);
} catch {
return null;
}
}

export function normalizeSearchText(
value: unknown,
{ fallback = '', arrayJoiner = ' ' }: NormalizeSearchTextOptions = {}
): string {
if (value === null || value === undefined) {
return fallback;
}

if (Array.isArray(value)) {
const normalized = value
.map(item =>
normalizeSearchText(item, {
fallback: '',
arrayJoiner,
})
)
.filter(Boolean);

return normalized.length > 0 ? normalized.join(arrayJoiner) : fallback;
}

if (typeof value !== 'string') {
return String(value);
}

const parsed = tryParseSerializedText(value);
if (parsed === null) {
return value;
}

if (typeof parsed === 'string' || Array.isArray(parsed)) {
return normalizeSearchText(parsed, {
fallback,
arrayJoiner,
});
}

return value;
}
Loading