search/related posts refinements

This commit is contained in:
keyan 2024-01-17 17:39:39 -06:00
parent f915b2b8b3
commit 61a66127d1
7 changed files with 64 additions and 31 deletions

View File

@ -109,7 +109,7 @@ export function joinZapRankPersonalView (me, models) {
// this grabs all the stuff we need to display the item list and only
// hits the db once ... orderBy needs to be duplicated on the outer query because
// joining does not preserve the order of the inner query
async function itemQueryWithMeta ({ me, models, query, orderBy = '' }, ...args) {
export async function itemQueryWithMeta ({ me, models, query, orderBy = '' }, ...args) {
if (!me) {
return await models.$queryRawUnsafe(`
SELECT "Item".*, to_json(users.*) as user, to_jsonb("Sub".*) as sub

View File

@ -1,6 +1,6 @@
import { decodeCursor, LIMIT, nextCursorEncoded } from '../../lib/cursor'
import { whenToFrom } from '../../lib/time'
import { getItem } from './item'
import { getItem, itemQueryWithMeta, SELECT } from './item'
function queryParts (q) {
const regex = /"([^"]*)"/gm
@ -58,7 +58,8 @@ export default {
max_doc_freq: 5,
min_word_length: 2,
max_query_terms: 25,
minimum_should_match: minMatch || '10%'
minimum_should_match: minMatch || '10%',
boost_terms: 100
}
}
]
@ -94,7 +95,7 @@ export default {
]
}
let items = await search.search({
const results = await search.search({
index: process.env.OPENSEARCH_INDEX,
size: limit,
from: decodedCursor.offset,
@ -141,9 +142,19 @@ export default {
}
})
items = items.body.hits.hits.map(async e => {
// this is super inefficient but will suffice until we do something more generic
return await getItem(parent, { id: e._source.id }, { me, models })
const values = results.body.hits.hits.map((e, i) => {
return `(${e._source.id}, ${i})`
}).join(',')
const items = await itemQueryWithMeta({
me,
models,
query: `
WITH r(id, rank) AS (VALUES ${values})
${SELECT}, rank
FROM "Item"
JOIN r ON "Item".id = r.id`,
orderBy: 'ORDER BY rank ASC'
})
return {
@ -406,14 +417,23 @@ export default {
}
}
// return highlights
const items = sitems.body.hits.hits.map(async e => {
// this is super inefficient but will suffice until we do something more generic
const item = await getItem(parent, { id: e._source.id }, { me, models })
const values = sitems.body.hits.hits.map((e, i) => {
return `(${e._source.id}, ${i})`
}).join(',')
const items = (await itemQueryWithMeta({
me,
models,
query: `
WITH r(id, rank) AS (VALUES ${values})
${SELECT}, rank
FROM "Item"
JOIN r ON "Item".id = r.id`,
orderBy: 'ORDER BY rank ASC'
})).map((item, i) => {
const e = sitems.body.hits.hits[i]
item.searchTitle = (e.highlight?.title && e.highlight.title[0]) || item.title
item.searchText = (e.highlight?.text && e.highlight.text.join(' ... ')) || undefined
return item
})

View File

@ -161,7 +161,7 @@ function TopLevelItem ({ item, noReply, ...props }) {
{!noReply &&
<>
<Reply item={item} replyOpen placeholder={item.ncomments > 3 ? 'fractions of a penny for your thoughts?' : 'early comments get more zaps'} onCancelQuote={cancelQuote} onQuoteReply={quoteReply} quote={quote} />
{!item.position && !item.isJob && !item.parentId && !item.bounty > 0 && <Related title={item.title} itemId={item.id} />}
{!item.position && !item.isJob && !item.parentId && !(item.bounty > 0) && <Related title={item.title} itemId={item.id} show={item.ncomments < 3} />}
{item.bounty > 0 && <PastBounties item={item} />}
</>}
</ItemComponent>

View File

@ -4,7 +4,7 @@
max-width: 100%;
display: inline-block;
vertical-align: middle;
padding-bottom: .15rem;
margin-bottom: .15rem;
}
.notification {
@ -35,7 +35,7 @@ a.title:visited {
overflow: hidden;
text-overflow: ellipsis;
flex: 1 0 128px;
padding-bottom: .15rem;
margin-bottom: .15rem;
}
.newComment {
@ -169,8 +169,10 @@ a.link:visited {
background-color: var(--theme-grey);
width: 500px;
border-radius: .4rem;
height: 17px;
margin: 0;
}
.skeleton .title::after {
content: "\00a0";
}
.skeleton .name {
@ -181,12 +183,19 @@ a.link:visited {
margin: 0;
}
.skeleton .hunk {
margin: 2px 0;
}
.skeleton .link {
height: 14px;
background-color: var(--theme-grey);
width: 800px;
border-radius: .3rem;
margin: 2px 0px;
line-height: .8rem;
}
.skeleton .link::after {
content: "\00a0";
}
.skeleton .otherItem {

View File

@ -28,7 +28,7 @@ export default function Items ({ ssrData, variables = {}, query, destructureData
pins?.reduce((a, p) => { a[p.position] = p; return a }, {}), [pins])
const Skeleton = useCallback(() =>
<ItemsSkeleton rank={rank} startRank={items?.length} limit={variables.limit} />, [rank, items])
<ItemsSkeleton rank={rank} startRank={items?.length} limit={variables.limit} Footer={Foooter} />, [rank, items])
if (!dat) {
return <Skeleton />
@ -65,14 +65,17 @@ export function ListItem ({ item, ...props }) {
)
}
export function ItemsSkeleton ({ rank, startRank = 0, limit = LIMIT }) {
export function ItemsSkeleton ({ rank, startRank = 0, limit = LIMIT, Footer }) {
const items = new Array(limit).fill(null)
return (
<>
<div className={styles.grid}>
{items.map((_, i) => (
<ItemSkeleton rank={rank && i + startRank + 1} key={i + startRank} />
))}
</div>
<Footer invisible cursor />
</>
)
}

View File

@ -2,7 +2,7 @@ import Button from 'react-bootstrap/Button'
import { useState } from 'react'
import Link from 'next/link'
export default function MoreFooter ({ cursor, count, fetchMore, Skeleton, noMoreText = 'GENESIS' }) {
export default function MoreFooter ({ cursor, count, fetchMore, Skeleton, invisible, noMoreText = 'GENESIS' }) {
const [loading, setLoading] = useState(false)
if (loading) {
@ -33,10 +33,10 @@ export default function MoreFooter ({ cursor, count, fetchMore, Skeleton, noMore
)
}
return <div className='d-flex justify-content-center mt-3 mb-1'><Footer /></div>
return <div className={`d-flex justify-content-center mt-3 mb-1 ${invisible ? 'invisible' : ''}`}><Footer /></div>
}
export function NavigateFooter ({ cursor, count, fetchMore, href, text, noMoreText = 'NO MORE' }) {
export function NavigateFooter ({ cursor, count, fetchMore, href, text, invisible, noMoreText = 'NO MORE' }) {
let Footer
if (cursor) {
Footer = () => (
@ -48,5 +48,5 @@ export function NavigateFooter ({ cursor, count, fetchMore, href, text, noMoreTe
)
}
return <div className='d-flex justify-content-start my-1'><Footer /></div>
return <div className={`d-flex justify-content-start my-1 ${invisible ? 'invisible' : ''}`}><Footer /></div>
}

View File

@ -5,7 +5,7 @@ import { NavigateFooter } from './more-footer'
const LIMIT = 5
export default function Related ({ title, itemId }) {
export default function Related ({ title, itemId, ...props }) {
const variables = { title, id: itemId, limit: LIMIT }
return (
<AccordianItem
@ -18,6 +18,7 @@ export default function Related ({ title, itemId }) {
Footer={props => <NavigateFooter {...props} href={`/items/${itemId}/related`} text='view all related items' />}
/>
}
{...props}
/>
)
}