From f0818edd6e1d566a3d7e2b5495eeb389d728f564 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 23 Dec 2017 07:21:52 +0900 Subject: [PATCH] #1037 #1038 --- src/api/endpoints/posts/search.ts | 138 +++++++----------- .../app/common/scripts/parse-search-query.ts | 5 +- src/web/docs/search.ja.pug | 19 ++- 3 files changed, 72 insertions(+), 90 deletions(-) diff --git a/src/api/endpoints/posts/search.ts b/src/api/endpoints/posts/search.ts index 26675989dd..31c9a8d3c8 100644 --- a/src/api/endpoints/posts/search.ts +++ b/src/api/endpoints/posts/search.ts @@ -1,7 +1,6 @@ /** * Module dependencies */ -import * as mongo from 'mongodb'; import $ from 'cafy'; const escapeRegexp = require('escape-regexp'); import Post from '../../models/post'; @@ -9,7 +8,6 @@ import User from '../../models/user'; import Mute from '../../models/mute'; import getFriends from '../../common/get-friends'; import serialize from '../../serializers/post'; -import config from '../../../conf'; /** * Search a post @@ -23,13 +21,21 @@ module.exports = (params, me) => new Promise(async (res, rej) => { const [text, textError] = $(params.text).optional.string().$; if (textError) return rej('invalid text param'); - // Get 'user_id' parameter - const [userId, userIdErr] = $(params.user_id).optional.id().$; - if (userIdErr) return rej('invalid user_id param'); + // Get 'include_user_ids' parameter + const [includeUserIds = [], includeUserIdsErr] = $(params.include_user_ids).optional.array('id').$; + if (includeUserIdsErr) return rej('invalid include_user_ids param'); - // Get 'username' parameter - const [username, usernameErr] = $(params.username).optional.string().$; - if (usernameErr) return rej('invalid username param'); + // Get 'exclude_user_ids' parameter + const [excludeUserIds = [], excludeUserIdsErr] = $(params.exclude_user_ids).optional.array('id').$; + if (excludeUserIdsErr) return rej('invalid exclude_user_ids param'); + + // Get 'include_user_usernames' parameter + const [includeUserUsernames = [], includeUserUsernamesErr] = $(params.include_user_usernames).optional.array('string').$; + if (includeUserUsernamesErr) return rej('invalid include_user_usernames param'); + + // Get 'exclude_user_usernames' parameter + const [excludeUserUsernames = [], excludeUserUsernamesErr] = $(params.exclude_user_usernames).optional.array('string').$; + if (excludeUserUsernamesErr) return rej('invalid exclude_user_usernames param'); // Get 'following' parameter const [following = null, followingErr] = $(params.following).optional.nullable.boolean().$; @@ -71,25 +77,36 @@ module.exports = (params, me) => new Promise(async (res, rej) => { const [limit = 10, limitErr] = $(params.limit).optional.number().range(1, 30).$; if (limitErr) return rej('invalid limit param'); - let user = userId; - - if (user == null && username != null) { - const _user = await User.findOne({ - username_lower: username.toLowerCase() - }); - if (_user) { - user = _user._id; - } + let includeUsers = includeUserIds; + if (includeUserUsernames != null) { + const ids = (await Promise.all(includeUserUsernames.map(async (username) => { + const _user = await User.findOne({ + username_lower: username.toLowerCase() + }); + return _user ? _user._id : null; + }))).filter(id => id != null); + includeUsers = includeUsers.concat(ids); } - // If Elasticsearch is available, search by it - // If not, search by MongoDB - (config.elasticsearch.enable ? byElasticsearch : byNative) - (res, rej, me, text, user, following, mute, reply, repost, media, poll, sinceDate, untilDate, offset, limit); + let excludeUsers = excludeUserIds; + if (excludeUserUsernames != null) { + const ids = (await Promise.all(excludeUserUsernames.map(async (username) => { + const _user = await User.findOne({ + username_lower: username.toLowerCase() + }); + return _user ? _user._id : null; + }))).filter(id => id != null); + excludeUsers = excludeUsers.concat(ids); + } + + search(res, rej, me, text, includeUsers, excludeUsers, following, + mute, reply, repost, media, poll, sinceDate, untilDate, offset, limit); }); -// Search by MongoDB -async function byNative(res, rej, me, text, userId, following, mute, reply, repost, media, poll, sinceDate, untilDate, offset, max) { +async function search( + res, rej, me, text, includeUserIds, excludeUserIds, following, + mute, reply, repost, media, poll, sinceDate, untilDate, offset, max) { + let q: any = { $and: [] }; @@ -115,9 +132,17 @@ async function byNative(res, rej, me, text, userId, following, mute, reply, repo } } - if (userId) { + if (includeUserIds && includeUserIds.length != 0) { push({ - user_id: userId + user_id: { + $in: includeUserIds + } + }); + } else if (excludeUserIds && excludeUserIds.length != 0) { + push({ + user_id: { + $nin: excludeUserIds + } }); } @@ -328,66 +353,3 @@ async function byNative(res, rej, me, text, userId, following, mute, reply, repo res(await Promise.all(posts.map(async post => await serialize(post, me)))); } - -// Search by Elasticsearch -async function byElasticsearch(res, rej, me, text, userId, following, mute, reply, repost, media, poll, sinceDate, untilDate, offset, max) { - const es = require('../../db/elasticsearch'); - - es.search({ - index: 'misskey', - type: 'post', - body: { - size: max, - from: offset, - query: { - simple_query_string: { - fields: ['text'], - query: text, - default_operator: 'and' - } - }, - sort: [ - { _doc: 'desc' } - ], - highlight: { - pre_tags: [''], - post_tags: [''], - encoder: 'html', - fields: { - text: {} - } - } - } - }, async (error, response) => { - if (error) { - console.error(error); - return res(500); - } - - if (response.hits.total === 0) { - return res([]); - } - - const hits = response.hits.hits.map(hit => new mongo.ObjectID(hit._id)); - - // Fetch found posts - const posts = await Post - .find({ - _id: { - $in: hits - } - }, { - sort: { - _id: -1 - } - }); - - posts.map(post => { - post._highlight = response.hits.hits.filter(hit => post._id.equals(hit._id))[0].highlight.text[0]; - }); - - // Serialize - res(await Promise.all(posts.map(async post => - await serialize(post, me)))); - }); -} diff --git a/src/web/app/common/scripts/parse-search-query.ts b/src/web/app/common/scripts/parse-search-query.ts index c021ee6417..512791ecb0 100644 --- a/src/web/app/common/scripts/parse-search-query.ts +++ b/src/web/app/common/scripts/parse-search-query.ts @@ -8,7 +8,10 @@ export default function(qs: string) { const [key, value] = x.split(':'); switch (key) { case 'user': - q['username'] = value; + q['include_user_usernames'] = value.split(','); + break; + case 'exclude_user': + q['exclude_user_usernames'] = value.split(','); break; case 'follow': q['following'] = value == 'null' ? null : value == 'true'; diff --git a/src/web/docs/search.ja.pug b/src/web/docs/search.ja.pug index 9e64789488..f33091ee6b 100644 --- a/src/web/docs/search.ja.pug +++ b/src/web/docs/search.ja.pug @@ -31,7 +31,24 @@ section tbody tr td user - td ユーザー名。投稿者を限定します。 + td + | 指定されたユーザー名のユーザーの投稿に限定します。 + | 「,」(カンマ)で区切って、複数ユーザーを指定することもできます。 + br + | 例えば、 + code user:himawari,sakurako + | と検索すると「@himawariまたは@sakurakoの投稿」だけに限定します。 + | (つまりユーザーのホワイトリストです) + tr + td exclude_user + td + | 指定されたユーザー名のユーザーの投稿を除外します。 + | 「,」(カンマ)で区切って、複数ユーザーを指定することもできます。 + br + | 例えば、 + code exclude_user:akari,chinatsu + | と検索すると「@akariまたは@chinatsu以外の投稿」に限定します。 + | (つまりユーザーのブラックリストです) tr td follow td