This commit is contained in:
syuilo 2017-12-23 07:21:52 +09:00
parent 7cf4aa9f11
commit f0818edd6e
3 changed files with 72 additions and 90 deletions

View file

@ -1,7 +1,6 @@
/** /**
* Module dependencies * Module dependencies
*/ */
import * as mongo from 'mongodb';
import $ from 'cafy'; import $ from 'cafy';
const escapeRegexp = require('escape-regexp'); const escapeRegexp = require('escape-regexp');
import Post from '../../models/post'; import Post from '../../models/post';
@ -9,7 +8,6 @@ import User from '../../models/user';
import Mute from '../../models/mute'; import Mute from '../../models/mute';
import getFriends from '../../common/get-friends'; import getFriends from '../../common/get-friends';
import serialize from '../../serializers/post'; import serialize from '../../serializers/post';
import config from '../../../conf';
/** /**
* Search a post * Search a post
@ -23,13 +21,21 @@ module.exports = (params, me) => new Promise(async (res, rej) => {
const [text, textError] = $(params.text).optional.string().$; const [text, textError] = $(params.text).optional.string().$;
if (textError) return rej('invalid text param'); if (textError) return rej('invalid text param');
// Get 'user_id' parameter // Get 'include_user_ids' parameter
const [userId, userIdErr] = $(params.user_id).optional.id().$; const [includeUserIds = [], includeUserIdsErr] = $(params.include_user_ids).optional.array('id').$;
if (userIdErr) return rej('invalid user_id param'); if (includeUserIdsErr) return rej('invalid include_user_ids param');
// Get 'username' parameter // Get 'exclude_user_ids' parameter
const [username, usernameErr] = $(params.username).optional.string().$; const [excludeUserIds = [], excludeUserIdsErr] = $(params.exclude_user_ids).optional.array('id').$;
if (usernameErr) return rej('invalid username param'); 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 // Get 'following' parameter
const [following = null, followingErr] = $(params.following).optional.nullable.boolean().$; 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).$; const [limit = 10, limitErr] = $(params.limit).optional.number().range(1, 30).$;
if (limitErr) return rej('invalid limit param'); if (limitErr) return rej('invalid limit param');
let user = userId; let includeUsers = includeUserIds;
if (includeUserUsernames != null) {
if (user == null && username != null) { const ids = (await Promise.all(includeUserUsernames.map(async (username) => {
const _user = await User.findOne({ const _user = await User.findOne({
username_lower: username.toLowerCase() username_lower: username.toLowerCase()
}); });
if (_user) { return _user ? _user._id : null;
user = _user._id; }))).filter(id => id != null);
} includeUsers = includeUsers.concat(ids);
} }
// If Elasticsearch is available, search by it let excludeUsers = excludeUserIds;
// If not, search by MongoDB if (excludeUserUsernames != null) {
(config.elasticsearch.enable ? byElasticsearch : byNative) const ids = (await Promise.all(excludeUserUsernames.map(async (username) => {
(res, rej, me, text, user, following, mute, reply, repost, media, poll, sinceDate, untilDate, offset, limit); 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 search(
async function byNative(res, rej, me, text, userId, following, mute, reply, repost, media, poll, sinceDate, untilDate, offset, max) { res, rej, me, text, includeUserIds, excludeUserIds, following,
mute, reply, repost, media, poll, sinceDate, untilDate, offset, max) {
let q: any = { let q: any = {
$and: [] $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({ 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 => res(await Promise.all(posts.map(async post =>
await serialize(post, me)))); 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: ['<mark>'],
post_tags: ['</mark>'],
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))));
});
}

View file

@ -8,7 +8,10 @@ export default function(qs: string) {
const [key, value] = x.split(':'); const [key, value] = x.split(':');
switch (key) { switch (key) {
case 'user': case 'user':
q['username'] = value; q['include_user_usernames'] = value.split(',');
break;
case 'exclude_user':
q['exclude_user_usernames'] = value.split(',');
break; break;
case 'follow': case 'follow':
q['following'] = value == 'null' ? null : value == 'true'; q['following'] = value == 'null' ? null : value == 'true';

View file

@ -31,7 +31,24 @@ section
tbody tbody
tr tr
td user 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 tr
td follow td follow
td td