add search tags
parent
8276a7cc3e
commit
e85d497d18
|
@ -22,12 +22,15 @@ module.exports = {
|
||||||
if (!body.metadata) throw new HttpError(400, 'Missing OPDS metadata')
|
if (!body.metadata) throw new HttpError(400, 'Missing OPDS metadata')
|
||||||
if (!body.metadata['@type'] || body.metadata['@type'] !== 'http://schema.org/Book') throw new HttpError(400, 'Invalid \'@type\': expected \'http://schema.org/Book\'')
|
if (!body.metadata['@type'] || body.metadata['@type'] !== 'http://schema.org/Book') throw new HttpError(400, 'Invalid \'@type\': expected \'http://schema.org/Book\'')
|
||||||
|
|
||||||
|
let tags = (body.metadata.tags || '').split(/,\s*/)
|
||||||
|
if (!tags.length && body.metadata.title) tags = body.metadata.title.split(/\s+/).filter(x => x.length < 3)
|
||||||
const query = {
|
const query = {
|
||||||
hostname: host,
|
hostname: host,
|
||||||
title: body.metadata.title,
|
title: body.metadata.title,
|
||||||
author: body.metadata.author,
|
author: body.metadata.author,
|
||||||
publisher: body.metadata.publisher,
|
publisher: body.metadata.publisher,
|
||||||
identifier: body.metadata.identifier,
|
identifier: body.metadata.identifier,
|
||||||
|
tags: JSON.stringify(tags),
|
||||||
version: body.metadata.modified.replace(/\D/g, '')
|
version: body.metadata.modified.replace(/\D/g, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +52,10 @@ module.exports = {
|
||||||
}
|
}
|
||||||
|
|
||||||
sendUpdatesAsync(result)
|
sendUpdatesAsync(result)
|
||||||
return res.json(result)
|
return res.json({
|
||||||
|
...result,
|
||||||
|
tags: JSON.parse(result.tags || '[]')
|
||||||
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof HttpError) return e.send(res)
|
if (e instanceof HttpError) return e.send(res)
|
||||||
return res.status(500).json({
|
return res.status(500).json({
|
||||||
|
@ -67,9 +73,8 @@ async function sendUpdatesAsync (book) {
|
||||||
try {
|
try {
|
||||||
const item = targets[i]
|
const item = targets[i]
|
||||||
const user = await User.findOne({ id: item.user })
|
const user = await User.findOne({ id: item.user })
|
||||||
const { author: fAuthor, publisher: fPublisher, title: fTitle, identifier: fIsbn, url } = item
|
const { author: fAuthor, publisher: fPublisher, title: fTitle, identifier: fIsbn, tags: fTags, url } = item
|
||||||
const { author: bAuthor, publisher: bPublisher, title: bTitle, identifier: bIsbn, opds } = book
|
const { author: bAuthor, publisher: bPublisher, title: bTitle, identifier: bIsbn, tags: bTags, opds } = book
|
||||||
sails.log('sending ' + book.id + ' info to ' + url)
|
|
||||||
|
|
||||||
if (uriRegex.test(url)) {
|
if (uriRegex.test(url)) {
|
||||||
if (fAuthor && !((bAuthor || '').includes(fAuthor))) continue
|
if (fAuthor && !((bAuthor || '').includes(fAuthor))) continue
|
||||||
|
@ -77,6 +82,13 @@ async function sendUpdatesAsync (book) {
|
||||||
if (fTitle && !((bTitle || '').includes(fTitle))) continue
|
if (fTitle && !((bTitle || '').includes(fTitle))) continue
|
||||||
if (fIsbn && !((bIsbn || '').includes(fIsbn))) continue
|
if (fIsbn && !((bIsbn || '').includes(fIsbn))) continue
|
||||||
|
|
||||||
|
const filterTags = JSON.parse(fTags || '[]')
|
||||||
|
if (filterTags.length && filterTags[0].length) {
|
||||||
|
const otherSet = new Set(filterTags)
|
||||||
|
if (!([...new Set(JSON.parse(bTags || '[]'))].filter(x => otherSet.has(x)).length)) continue
|
||||||
|
}
|
||||||
|
sails.log('sending ' + book.id + ' info to ' + url)
|
||||||
|
|
||||||
let content = opds
|
let content = opds
|
||||||
const timestamp = Date.now()
|
const timestamp = Date.now()
|
||||||
request.post({
|
request.post({
|
||||||
|
|
|
@ -42,7 +42,17 @@ module.exports = {
|
||||||
page = Math.abs(+body.page) || 1
|
page = Math.abs(+body.page) || 1
|
||||||
delete body.page
|
delete body.page
|
||||||
}
|
}
|
||||||
let books = await Book.find(body || {}).skip((page * perPage) - perPage).limit(perPage)
|
const searchBody = { ...body }
|
||||||
|
if (searchBody.tags) {
|
||||||
|
const tags = searchBody.tags.split(/,\s*/)
|
||||||
|
searchBody.tags = {
|
||||||
|
or: [
|
||||||
|
...tags.map(tag => ({ contains: tag })),
|
||||||
|
{ in: tags }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let books = await Book.find(body ? searchBody : {}).skip((page * perPage) - perPage).limit(perPage)
|
||||||
|
|
||||||
if (!books.length) {
|
if (!books.length) {
|
||||||
throw new HttpError(404, 'No books matching those parameters were found.')
|
throw new HttpError(404, 'No books matching those parameters were found.')
|
||||||
|
|
|
@ -25,13 +25,15 @@ module.exports = {
|
||||||
const publisher = req.param('publisher') || ''
|
const publisher = req.param('publisher') || ''
|
||||||
const title = req.param('title') || ''
|
const title = req.param('title') || ''
|
||||||
const isbn = req.param('isbn') || ''
|
const isbn = req.param('isbn') || ''
|
||||||
|
const tags = req.param('tags') || ''
|
||||||
if (value.length) {
|
if (value.length) {
|
||||||
const url = await TargetUrl.update({ id, user: req.user.id }, {
|
const url = await TargetUrl.update({ id, user: req.user.id }, {
|
||||||
url: value,
|
url: value,
|
||||||
author,
|
author,
|
||||||
publisher,
|
publisher,
|
||||||
title,
|
title,
|
||||||
isbn
|
isbn,
|
||||||
|
tags: JSON.stringify(tags.split(/,\s*/))
|
||||||
}).fetch()
|
}).fetch()
|
||||||
return res.json(url)
|
return res.json(url)
|
||||||
} else {
|
} else {
|
||||||
|
@ -52,9 +54,13 @@ module.exports = {
|
||||||
},
|
},
|
||||||
list: async function (req, res) {
|
list: async function (req, res) {
|
||||||
try {
|
try {
|
||||||
const urls = await TargetUrl.find({
|
let urls = await TargetUrl.find({
|
||||||
user: req.user.id
|
user: req.user.id
|
||||||
})
|
})
|
||||||
|
urls = urls.map(url => ({
|
||||||
|
...url,
|
||||||
|
tags: JSON.parse(url.tags || '[]')
|
||||||
|
}))
|
||||||
return res.json(urls)
|
return res.json(urls)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return (new HttpError(500, e.message)).send(res)
|
return (new HttpError(500, e.message)).send(res)
|
||||||
|
|
|
@ -23,7 +23,8 @@ module.exports = {
|
||||||
identifier: { type: 'string' },
|
identifier: { type: 'string' },
|
||||||
version: { type: 'string' },
|
version: { type: 'string' },
|
||||||
hostname: { type: 'string' },
|
hostname: { type: 'string' },
|
||||||
opds: { type: 'json' }
|
opds: { type: 'json' },
|
||||||
|
tags: { type: 'string' }
|
||||||
|
|
||||||
// ╔═╗╔╦╗╔╗ ╔═╗╔╦╗╔═╗
|
// ╔═╗╔╦╗╔╗ ╔═╗╔╦╗╔═╗
|
||||||
// ║╣ ║║║╠╩╗║╣ ║║╚═╗
|
// ║╣ ║║║╠╩╗║╣ ║║╚═╗
|
||||||
|
|
|
@ -15,6 +15,7 @@ module.exports = {
|
||||||
author: 'string',
|
author: 'string',
|
||||||
publisher: 'string',
|
publisher: 'string',
|
||||||
title: 'string',
|
title: 'string',
|
||||||
isbn: 'string'
|
isbn: 'string',
|
||||||
|
tags: 'string'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ class UriListItem extends React.Component {
|
||||||
</div>
|
</div>
|
||||||
<div className='col stack flex flex-container flex-vertical'>
|
<div className='col stack flex flex-container flex-vertical'>
|
||||||
<span className='label'>Filters</span>
|
<span className='label'>Filters</span>
|
||||||
<span className='value'>{['publisher', 'title', 'author', 'isbn'].reduce((a, x) => a + (this.props.item[x] ? 1 : 0), 0) || 'None'}</span>
|
<span className='value'>{['publisher', 'title', 'author', 'isbn', 'tags'].reduce((a, x) => a + (this.props.item[x] ? 1 : 0), 0) || 'None'}</span>
|
||||||
</div>
|
</div>
|
||||||
<ConfirmIconButton icon='delete' onClick={() => this.props.dispatch(removeUrl(this.props.item.id))} />
|
<ConfirmIconButton icon='delete' onClick={() => this.props.dispatch(removeUrl(this.props.item.id))} />
|
||||||
</li>
|
</li>
|
||||||
|
@ -86,6 +86,15 @@ class UriListItem extends React.Component {
|
||||||
value={'' + this.props.item.isbn}
|
value={'' + this.props.item.isbn}
|
||||||
onChange={(e) => this.props.dispatch(changeUrlField(this.props.item.id, 'isbn', e.target.value))}
|
onChange={(e) => this.props.dispatch(changeUrlField(this.props.item.id, 'isbn', e.target.value))}
|
||||||
onBlur={(e) => this.props.dispatch(setUrl(this.props.item))} />
|
onBlur={(e) => this.props.dispatch(setUrl(this.props.item))} />
|
||||||
|
<UnderlineInput
|
||||||
|
className='uri flex'
|
||||||
|
type='text'
|
||||||
|
name={'tags-' + this.props.item.id}
|
||||||
|
placeholder='Tags'
|
||||||
|
value={this.props.item.tags ? this.props.item.tags.join(', ') : ''}
|
||||||
|
pattern={/^.*?(?:,\s+\S.+?)*$/}
|
||||||
|
onChange={(e) => this.props.dispatch(changeUrlField(this.props.item.id, 'tags', e.target.value.split(/,\s+/)))}
|
||||||
|
onBlur={(e) => this.props.dispatch(setUrl(this.props.item))} />
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
)
|
)
|
||||||
|
|
|
@ -18,6 +18,7 @@ and opds2 publication body with type `application/json`:
|
||||||
"title": "Moby-Dick",
|
"title": "Moby-Dick",
|
||||||
"author": "Herman Melville",
|
"author": "Herman Melville",
|
||||||
"identifier": "urn:isbn:978031600000X",
|
"identifier": "urn:isbn:978031600000X",
|
||||||
|
"tags": ["story", "classic"],
|
||||||
"publisher": "Ebook Publisher.com",
|
"publisher": "Ebook Publisher.com",
|
||||||
"language": "en",
|
"language": "en",
|
||||||
"modified": "2015-09-29T17:00:00Z"
|
"modified": "2015-09-29T17:00:00Z"
|
||||||
|
@ -46,6 +47,7 @@ The server will respond with either:
|
||||||
"id": number,
|
"id": number,
|
||||||
"title": string,
|
"title": string,
|
||||||
"author": string,
|
"author": string,
|
||||||
|
"tags": array,
|
||||||
"publisher": string,
|
"publisher": string,
|
||||||
"identifier": string,
|
"identifier": string,
|
||||||
"version": string,
|
"version": string,
|
||||||
|
@ -72,6 +74,7 @@ title: The ebook's title (optional)
|
||||||
author: The author (optional)
|
author: The author (optional)
|
||||||
version: A version number (optional)
|
version: A version number (optional)
|
||||||
isbn: The ISBN (optional)
|
isbn: The ISBN (optional)
|
||||||
|
tags: Comma-separated search tags (optional)
|
||||||
|
|
||||||
page: The page of results to view (200 results per page)
|
page: The page of results to view (200 results per page)
|
||||||
```
|
```
|
||||||
|
@ -107,6 +110,7 @@ The server will respond with either:
|
||||||
"@type": "http://schema.org/Book",
|
"@type": "http://schema.org/Book",
|
||||||
"title": "Moby-Dick",
|
"title": "Moby-Dick",
|
||||||
"author": "Herman Melville",
|
"author": "Herman Melville",
|
||||||
|
"tags": ["story", "classic"],
|
||||||
"publisher": "Ebook Publisher.com",
|
"publisher": "Ebook Publisher.com",
|
||||||
"identifier": "urn:isbn:978031600000X",
|
"identifier": "urn:isbn:978031600000X",
|
||||||
"language": "en",
|
"language": "en",
|
||||||
|
@ -165,6 +169,7 @@ HTTP Body:
|
||||||
"@type": "http://schema.org/Book",
|
"@type": "http://schema.org/Book",
|
||||||
"title": "Moby-Dick",
|
"title": "Moby-Dick",
|
||||||
"author": "Herman Melville",
|
"author": "Herman Melville",
|
||||||
|
"tags": ["story", "classic"],
|
||||||
"publisher": "Ebook Publisher.com",
|
"publisher": "Ebook Publisher.com",
|
||||||
"identifier": "urn:isbn:978031600000X",
|
"identifier": "urn:isbn:978031600000X",
|
||||||
"language": "en",
|
"language": "en",
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
exports.up = function (knex, Promise) {
|
||||||
|
return Promise.all([
|
||||||
|
knex.schema.table('book', t => {
|
||||||
|
t.string('tags')
|
||||||
|
}),
|
||||||
|
knex.schema.table('targeturl', t => {
|
||||||
|
t.string('tags')
|
||||||
|
})
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.down = function (knex, Promise) {
|
||||||
|
return Promise.all([
|
||||||
|
knex.schema.table('book', t => {
|
||||||
|
t.dropColumns('tags')
|
||||||
|
}),
|
||||||
|
knex.schema.table('targeturl', t => {
|
||||||
|
t.dropColumns('tags')
|
||||||
|
})
|
||||||
|
])
|
||||||
|
}
|
Loading…
Reference in New Issue