search/related posts refinements
This commit is contained in:
parent
f915b2b8b3
commit
61a66127d1
@ -109,7 +109,7 @@ export function joinZapRankPersonalView (me, models) {
|
|||||||
// this grabs all the stuff we need to display the item list and only
|
// 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
|
// hits the db once ... orderBy needs to be duplicated on the outer query because
|
||||||
// joining does not preserve the order of the inner query
|
// 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) {
|
if (!me) {
|
||||||
return await models.$queryRawUnsafe(`
|
return await models.$queryRawUnsafe(`
|
||||||
SELECT "Item".*, to_json(users.*) as user, to_jsonb("Sub".*) as sub
|
SELECT "Item".*, to_json(users.*) as user, to_jsonb("Sub".*) as sub
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { decodeCursor, LIMIT, nextCursorEncoded } from '../../lib/cursor'
|
import { decodeCursor, LIMIT, nextCursorEncoded } from '../../lib/cursor'
|
||||||
import { whenToFrom } from '../../lib/time'
|
import { whenToFrom } from '../../lib/time'
|
||||||
import { getItem } from './item'
|
import { getItem, itemQueryWithMeta, SELECT } from './item'
|
||||||
|
|
||||||
function queryParts (q) {
|
function queryParts (q) {
|
||||||
const regex = /"([^"]*)"/gm
|
const regex = /"([^"]*)"/gm
|
||||||
@ -58,7 +58,8 @@ export default {
|
|||||||
max_doc_freq: 5,
|
max_doc_freq: 5,
|
||||||
min_word_length: 2,
|
min_word_length: 2,
|
||||||
max_query_terms: 25,
|
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,
|
index: process.env.OPENSEARCH_INDEX,
|
||||||
size: limit,
|
size: limit,
|
||||||
from: decodedCursor.offset,
|
from: decodedCursor.offset,
|
||||||
@ -141,9 +142,19 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
items = items.body.hits.hits.map(async e => {
|
const values = results.body.hits.hits.map((e, i) => {
|
||||||
// this is super inefficient but will suffice until we do something more generic
|
return `(${e._source.id}, ${i})`
|
||||||
return await getItem(parent, { id: e._source.id }, { me, models })
|
}).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 {
|
return {
|
||||||
@ -406,14 +417,23 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// return highlights
|
const values = sitems.body.hits.hits.map((e, i) => {
|
||||||
const items = sitems.body.hits.hits.map(async e => {
|
return `(${e._source.id}, ${i})`
|
||||||
// this is super inefficient but will suffice until we do something more generic
|
}).join(',')
|
||||||
const item = await getItem(parent, { id: e._source.id }, { me, models })
|
|
||||||
|
|
||||||
|
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.searchTitle = (e.highlight?.title && e.highlight.title[0]) || item.title
|
||||||
item.searchText = (e.highlight?.text && e.highlight.text.join(' ... ')) || undefined
|
item.searchText = (e.highlight?.text && e.highlight.text.join(' ... ')) || undefined
|
||||||
|
|
||||||
return item
|
return item
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -161,7 +161,7 @@ function TopLevelItem ({ item, noReply, ...props }) {
|
|||||||
{!noReply &&
|
{!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} />
|
<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} />}
|
{item.bounty > 0 && <PastBounties item={item} />}
|
||||||
</>}
|
</>}
|
||||||
</ItemComponent>
|
</ItemComponent>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
padding-bottom: .15rem;
|
margin-bottom: .15rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification {
|
.notification {
|
||||||
@ -35,7 +35,7 @@ a.title:visited {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
flex: 1 0 128px;
|
flex: 1 0 128px;
|
||||||
padding-bottom: .15rem;
|
margin-bottom: .15rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.newComment {
|
.newComment {
|
||||||
@ -169,8 +169,10 @@ a.link:visited {
|
|||||||
background-color: var(--theme-grey);
|
background-color: var(--theme-grey);
|
||||||
width: 500px;
|
width: 500px;
|
||||||
border-radius: .4rem;
|
border-radius: .4rem;
|
||||||
height: 17px;
|
}
|
||||||
margin: 0;
|
|
||||||
|
.skeleton .title::after {
|
||||||
|
content: "\00a0";
|
||||||
}
|
}
|
||||||
|
|
||||||
.skeleton .name {
|
.skeleton .name {
|
||||||
@ -181,12 +183,19 @@ a.link:visited {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.skeleton .hunk {
|
||||||
|
margin: 2px 0;
|
||||||
|
}
|
||||||
|
|
||||||
.skeleton .link {
|
.skeleton .link {
|
||||||
height: 14px;
|
|
||||||
background-color: var(--theme-grey);
|
background-color: var(--theme-grey);
|
||||||
width: 800px;
|
width: 800px;
|
||||||
border-radius: .3rem;
|
border-radius: .3rem;
|
||||||
margin: 2px 0px;
|
line-height: .8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton .link::after {
|
||||||
|
content: "\00a0";
|
||||||
}
|
}
|
||||||
|
|
||||||
.skeleton .otherItem {
|
.skeleton .otherItem {
|
||||||
|
@ -28,7 +28,7 @@ export default function Items ({ ssrData, variables = {}, query, destructureData
|
|||||||
pins?.reduce((a, p) => { a[p.position] = p; return a }, {}), [pins])
|
pins?.reduce((a, p) => { a[p.position] = p; return a }, {}), [pins])
|
||||||
|
|
||||||
const Skeleton = useCallback(() =>
|
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) {
|
if (!dat) {
|
||||||
return <Skeleton />
|
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)
|
const items = new Array(limit).fill(null)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<div className={styles.grid}>
|
<div className={styles.grid}>
|
||||||
{items.map((_, i) => (
|
{items.map((_, i) => (
|
||||||
<ItemSkeleton rank={rank && i + startRank + 1} key={i + startRank} />
|
<ItemSkeleton rank={rank && i + startRank + 1} key={i + startRank} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
<Footer invisible cursor />
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import Button from 'react-bootstrap/Button'
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import Link from 'next/link'
|
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)
|
const [loading, setLoading] = useState(false)
|
||||||
|
|
||||||
if (loading) {
|
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
|
let Footer
|
||||||
if (cursor) {
|
if (cursor) {
|
||||||
Footer = () => (
|
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>
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import { NavigateFooter } from './more-footer'
|
|||||||
|
|
||||||
const LIMIT = 5
|
const LIMIT = 5
|
||||||
|
|
||||||
export default function Related ({ title, itemId }) {
|
export default function Related ({ title, itemId, ...props }) {
|
||||||
const variables = { title, id: itemId, limit: LIMIT }
|
const variables = { title, id: itemId, limit: LIMIT }
|
||||||
return (
|
return (
|
||||||
<AccordianItem
|
<AccordianItem
|
||||||
@ -18,6 +18,7 @@ export default function Related ({ title, itemId }) {
|
|||||||
Footer={props => <NavigateFooter {...props} href={`/items/${itemId}/related`} text='view all related items' />}
|
Footer={props => <NavigateFooter {...props} href={`/items/${itemId}/related`} text='view all related items' />}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
{...props}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user