mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 11:29:46 +00:00
feat: show users you follow first in search
getPubkeysBySearch() function refactored to accept a followList argument
This commit is contained in:
parent
2fe6a8fde5
commit
52001373e0
3 changed files with 43 additions and 15 deletions
|
|
@ -117,6 +117,7 @@ const accountSearchController: AppController = async (c) => {
|
||||||
const { signal } = c.req.raw;
|
const { signal } = c.req.raw;
|
||||||
const { limit } = c.get('pagination');
|
const { limit } = c.get('pagination');
|
||||||
const kysely = await Storages.kysely();
|
const kysely = await Storages.kysely();
|
||||||
|
const viewerPubkey = await c.get('signer')?.getPublicKey();
|
||||||
|
|
||||||
const result = accountSearchQuerySchema.safeParse(c.req.query());
|
const result = accountSearchQuerySchema.safeParse(c.req.query());
|
||||||
|
|
||||||
|
|
@ -135,7 +136,11 @@ const accountSearchController: AppController = async (c) => {
|
||||||
return c.json(pubkey ? [await accountFromPubkey(pubkey)] : []);
|
return c.json(pubkey ? [await accountFromPubkey(pubkey)] : []);
|
||||||
}
|
}
|
||||||
|
|
||||||
const pubkeys = await getPubkeysBySearch(kysely, { q: query, limit });
|
const followList: string[] = [];
|
||||||
|
if (viewerPubkey) {
|
||||||
|
followList.push(...await getFollowedPubkeys(viewerPubkey));
|
||||||
|
}
|
||||||
|
const pubkeys = (await getPubkeysBySearch(kysely, { q: query, limit, followList })).slice(0, limit);
|
||||||
|
|
||||||
let events = event ? [event] : await store.query([{ kinds: [0], authors: pubkeys, limit }], {
|
let events = event ? [event] : await store.query([{ kinds: [0], authors: pubkeys, limit }], {
|
||||||
signal,
|
signal,
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import { extractIdentifier, lookupPubkey } from '@/utils/lookup.ts';
|
||||||
import { nip05Cache } from '@/utils/nip05.ts';
|
import { nip05Cache } from '@/utils/nip05.ts';
|
||||||
import { accountFromPubkey, renderAccount } from '@/views/mastodon/accounts.ts';
|
import { accountFromPubkey, renderAccount } from '@/views/mastodon/accounts.ts';
|
||||||
import { renderStatus } from '@/views/mastodon/statuses.ts';
|
import { renderStatus } from '@/views/mastodon/statuses.ts';
|
||||||
|
import { getFollowedPubkeys } from '@/queries.ts';
|
||||||
import { getPubkeysBySearch } from '@/utils/search.ts';
|
import { getPubkeysBySearch } from '@/utils/search.ts';
|
||||||
|
|
||||||
const searchQuerySchema = z.object({
|
const searchQuerySchema = z.object({
|
||||||
|
|
@ -26,6 +27,7 @@ type SearchQuery = z.infer<typeof searchQuerySchema>;
|
||||||
const searchController: AppController = async (c) => {
|
const searchController: AppController = async (c) => {
|
||||||
const result = searchQuerySchema.safeParse(c.req.query());
|
const result = searchQuerySchema.safeParse(c.req.query());
|
||||||
const { signal } = c.req.raw;
|
const { signal } = c.req.raw;
|
||||||
|
const viewerPubkey = await c.get('signer')?.getPublicKey();
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
return c.json({ error: 'Bad request', schema: result.error }, 422);
|
return c.json({ error: 'Bad request', schema: result.error }, 422);
|
||||||
|
|
@ -49,9 +51,7 @@ const searchController: AppController = async (c) => {
|
||||||
if (event) {
|
if (event) {
|
||||||
events = [event];
|
events = [event];
|
||||||
}
|
}
|
||||||
events.push(...(await searchEvents(result.data, signal)));
|
events.push(...(await searchEvents({ ...result.data, viewerPubkey }, signal)));
|
||||||
|
|
||||||
const viewerPubkey = await c.get('signer')?.getPublicKey();
|
|
||||||
|
|
||||||
const [accounts, statuses] = await Promise.all([
|
const [accounts, statuses] = await Promise.all([
|
||||||
Promise.all(
|
Promise.all(
|
||||||
|
|
@ -76,7 +76,10 @@ const searchController: AppController = async (c) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Get events for the search params. */
|
/** Get events for the search params. */
|
||||||
async function searchEvents({ q, type, limit, account_id }: SearchQuery, signal: AbortSignal): Promise<NostrEvent[]> {
|
async function searchEvents(
|
||||||
|
{ q, type, limit, account_id, viewerPubkey }: SearchQuery & { viewerPubkey?: string },
|
||||||
|
signal: AbortSignal,
|
||||||
|
): Promise<NostrEvent[]> {
|
||||||
if (type === 'hashtags') return Promise.resolve([]);
|
if (type === 'hashtags') return Promise.resolve([]);
|
||||||
|
|
||||||
const filter: NostrFilter = {
|
const filter: NostrFilter = {
|
||||||
|
|
@ -93,7 +96,11 @@ async function searchEvents({ q, type, limit, account_id }: SearchQuery, signal:
|
||||||
if (type === 'accounts') {
|
if (type === 'accounts') {
|
||||||
const kysely = await Storages.kysely();
|
const kysely = await Storages.kysely();
|
||||||
|
|
||||||
pubkeys.push(...(await getPubkeysBySearch(kysely, { q, limit })));
|
const followList: string[] = [];
|
||||||
|
if (viewerPubkey) {
|
||||||
|
followList.push(...await getFollowedPubkeys(viewerPubkey));
|
||||||
|
}
|
||||||
|
pubkeys.push(...(await getPubkeysBySearch(kysely, { q, limit, followList })));
|
||||||
|
|
||||||
if (!filter?.authors) {
|
if (!filter?.authors) {
|
||||||
filter.authors = pubkeys;
|
filter.authors = pubkeys;
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,30 @@ import { Kysely, sql } from 'kysely';
|
||||||
import { DittoTables } from '@/db/DittoTables.ts';
|
import { DittoTables } from '@/db/DittoTables.ts';
|
||||||
|
|
||||||
/** Get pubkeys whose name and NIP-05 is similar to 'q' */
|
/** Get pubkeys whose name and NIP-05 is similar to 'q' */
|
||||||
export async function getPubkeysBySearch(kysely: Kysely<DittoTables>, opts: { q: string; limit: number }) {
|
export async function getPubkeysBySearch(
|
||||||
const { q, limit } = opts;
|
kysely: Kysely<DittoTables>,
|
||||||
const pubkeys = (await sql<{ pubkey: string }>`
|
opts: { q: string; limit: number; followList: string[] },
|
||||||
SELECT *, word_similarity(${q}, search) AS sml
|
) {
|
||||||
FROM author_search
|
const { q, limit, followList } = opts;
|
||||||
WHERE ${q} % search
|
|
||||||
ORDER BY sml DESC, search LIMIT ${limit}
|
|
||||||
`.execute(kysely)).rows.map(({ pubkey }) => pubkey);
|
|
||||||
|
|
||||||
return pubkeys;
|
let query = kysely
|
||||||
|
.selectFrom('author_search')
|
||||||
|
.select((eb) => [
|
||||||
|
'pubkey',
|
||||||
|
'search',
|
||||||
|
eb.fn('word_similarity', [sql`${q}`, 'search']).as('sml'),
|
||||||
|
])
|
||||||
|
.where(() => sql`${q} % search`)
|
||||||
|
.orderBy(['sml desc', 'search'])
|
||||||
|
.limit(limit);
|
||||||
|
|
||||||
|
const pubkeys = new Set((await query.execute()).map(({ pubkey }) => pubkey));
|
||||||
|
|
||||||
|
if (followList.length > 0) {
|
||||||
|
query = query.where('pubkey', 'in', followList);
|
||||||
|
}
|
||||||
|
|
||||||
|
const followingPubkeys = new Set((await query.execute()).map(({ pubkey }) => pubkey));
|
||||||
|
|
||||||
|
return Array.from(followingPubkeys.union(pubkeys));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue