summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorsyuilo <syuilotan@yahoo.co.jp>2019-04-25 07:46:39 +0900
committersyuilo <syuilotan@yahoo.co.jp>2019-04-25 07:46:39 +0900
commit0db54386cdef3f444f1afb4f3b8bfcaeab7ac68d (patch)
tree38d3d6b2112326bb1a03a8732cd8969e24d693de /src
parentFix #4793 (diff)
downloadsharkey-0db54386cdef3f444f1afb4f3b8bfcaeab7ac68d.tar.gz
sharkey-0db54386cdef3f444f1afb4f3b8bfcaeab7ac68d.tar.bz2
sharkey-0db54386cdef3f444f1afb4f3b8bfcaeab7ac68d.zip
Resolve #3119
Diffstat (limited to 'src')
-rw-r--r--src/client/app/common/scripts/gen-search-query.ts31
-rw-r--r--src/client/app/common/scripts/search.ts2
-rw-r--r--src/client/app/common/views/components/user-list.vue8
-rw-r--r--src/client/app/common/views/deck/deck.notes.vue8
-rw-r--r--src/client/app/common/views/deck/deck.search-column.vue5
-rw-r--r--src/client/app/desktop/views/components/notes.vue6
-rw-r--r--src/client/app/desktop/views/home/search.vue5
-rw-r--r--src/client/app/mobile/views/components/notes.vue8
-rw-r--r--src/client/app/mobile/views/pages/search.vue5
-rw-r--r--src/db/elasticsearch.ts72
-rw-r--r--src/server/api/endpoints/notes/search.ts64
-rw-r--r--src/services/note/create.ts7
12 files changed, 133 insertions, 88 deletions
diff --git a/src/client/app/common/scripts/gen-search-query.ts b/src/client/app/common/scripts/gen-search-query.ts
new file mode 100644
index 0000000000..fc26cb7f78
--- /dev/null
+++ b/src/client/app/common/scripts/gen-search-query.ts
@@ -0,0 +1,31 @@
+import parseAcct from '../../../../misc/acct/parse';
+import { host as localHost } from '../../config';
+
+export async function genSearchQuery(v: any, q: string) {
+ let host: string;
+ let userId: string;
+ if (q.split(' ').some(x => x.startsWith('@'))) {
+ for (const at of q.split(' ').filter(x => x.startsWith('@')).map(x => x.substr(1))) {
+ if (at.includes('.')) {
+ if (at === localHost || at === '.') {
+ host = null;
+ } else {
+ host = at;
+ }
+ } else {
+ const user = await v.$root.api('users/show', parseAcct(at)).catch(x => null);
+ if (user) {
+ userId = user.id;
+ } else {
+ // todo: show error
+ }
+ }
+ }
+
+ }
+ return {
+ query: q.split(' ').filter(x => !x.startsWith('/') && !x.startsWith('@')).join(' '),
+ host: host,
+ userId: userId
+ };
+}
diff --git a/src/client/app/common/scripts/search.ts b/src/client/app/common/scripts/search.ts
index c44581817b..2897ed6318 100644
--- a/src/client/app/common/scripts/search.ts
+++ b/src/client/app/common/scripts/search.ts
@@ -3,7 +3,7 @@ import { faHistory } from '@fortawesome/free-solid-svg-icons';
export async function search(v: any, q: string) {
q = q.trim();
- if (q.startsWith('@')) {
+ if (q.startsWith('@') && !q.includes(' ')) {
v.$router.push(`/${q}`);
return;
}
diff --git a/src/client/app/common/views/components/user-list.vue b/src/client/app/common/views/components/user-list.vue
index b8bcc35d82..53577bad00 100644
--- a/src/client/app/common/views/components/user-list.vue
+++ b/src/client/app/common/views/components/user-list.vue
@@ -60,9 +60,9 @@ export default Vue.extend({
},
methods: {
- init() {
+ async init() {
this.fetching = true;
- this.makePromise().then(x => {
+ await (this.makePromise()).then(x => {
if (Array.isArray(x)) {
this.us = x;
} else {
@@ -76,9 +76,9 @@ export default Vue.extend({
});
},
- fetchMoreUsers() {
+ async fetchMoreUsers() {
this.fetchingMoreUsers = true;
- this.makePromise(this.cursor).then(x => {
+ await (this.makePromise(this.cursor)).then(x => {
this.us = this.us.concat(x.users);
this.cursor = x.cursor;
this.fetchingMoreUsers = false;
diff --git a/src/client/app/common/views/deck/deck.notes.vue b/src/client/app/common/views/deck/deck.notes.vue
index bc67f4911c..680b44bc81 100644
--- a/src/client/app/common/views/deck/deck.notes.vue
+++ b/src/client/app/common/views/deck/deck.notes.vue
@@ -110,11 +110,11 @@ export default Vue.extend({
this.init();
},
- init() {
+ async init() {
this.queue = [];
this.notes = [];
this.fetching = true;
- this.makePromise().then(x => {
+ await (this.makePromise()).then(x => {
if (Array.isArray(x)) {
this.notes = x;
} else {
@@ -129,10 +129,10 @@ export default Vue.extend({
});
},
- fetchMore() {
+ async fetchMore() {
if (!this.more || this.moreFetching) return;
this.moreFetching = true;
- this.makePromise(this.notes[this.notes.length - 1].id).then(x => {
+ await (this.makePromise(this.notes[this.notes.length - 1].id)).then(x => {
this.notes = this.notes.concat(x.notes);
this.more = x.more;
this.moreFetching = false;
diff --git a/src/client/app/common/views/deck/deck.search-column.vue b/src/client/app/common/views/deck/deck.search-column.vue
index ab19bdaab6..17ee2ef454 100644
--- a/src/client/app/common/views/deck/deck.search-column.vue
+++ b/src/client/app/common/views/deck/deck.search-column.vue
@@ -14,6 +14,7 @@
import Vue from 'vue';
import XColumn from './deck.column.vue';
import XNotes from './deck.notes.vue';
+import { genSearchQuery } from '../../../common/scripts/gen-search-query';
const limit = 20;
@@ -25,10 +26,10 @@ export default Vue.extend({
data() {
return {
- makePromise: cursor => this.$root.api('notes/search', {
+ makePromise: async cursor => this.$root.api('notes/search', {
limit: limit + 1,
offset: cursor ? cursor : undefined,
- query: this.q
+ ...(await genSearchQuery(this, this.q))
}).then(notes => {
if (notes.length == limit + 1) {
notes.pop();
diff --git a/src/client/app/desktop/views/components/notes.vue b/src/client/app/desktop/views/components/notes.vue
index 9044ad3478..87fdc749de 100644
--- a/src/client/app/desktop/views/components/notes.vue
+++ b/src/client/app/desktop/views/components/notes.vue
@@ -105,9 +105,9 @@ export default Vue.extend({
this.init();
},
- init() {
+ async init() {
this.fetching = true;
- this.makePromise().then(x => {
+ await (this.makePromise()).then(x => {
if (Array.isArray(x)) {
this.notes = x;
} else {
@@ -122,7 +122,7 @@ export default Vue.extend({
});
},
- fetchMore() {
+ async fetchMore() {
if (!this.more || this.moreFetching || this.notes.length === 0) return;
this.moreFetching = true;
this.makePromise(this.notes[this.notes.length - 1].id).then(x => {
diff --git a/src/client/app/desktop/views/home/search.vue b/src/client/app/desktop/views/home/search.vue
index 84153d18c4..50c6456158 100644
--- a/src/client/app/desktop/views/home/search.vue
+++ b/src/client/app/desktop/views/home/search.vue
@@ -14,6 +14,7 @@
import Vue from 'vue';
import i18n from '../../../i18n';
import Progress from '../../../common/scripts/loading';
+import { genSearchQuery } from '../../../common/scripts/gen-search-query';
const limit = 20;
@@ -21,10 +22,10 @@ export default Vue.extend({
i18n: i18n('desktop/views/pages/search.vue'),
data() {
return {
- makePromise: cursor => this.$root.api('notes/search', {
+ makePromise: async cursor => this.$root.api('notes/search', {
limit: limit + 1,
offset: cursor ? cursor : undefined,
- query: this.q
+ ...(await genSearchQuery(this, this.q))
}).then(notes => {
if (notes.length == limit + 1) {
notes.pop();
diff --git a/src/client/app/mobile/views/components/notes.vue b/src/client/app/mobile/views/components/notes.vue
index 2e42300717..5ad80c286d 100644
--- a/src/client/app/mobile/views/components/notes.vue
+++ b/src/client/app/mobile/views/components/notes.vue
@@ -106,9 +106,9 @@ export default Vue.extend({
this.init();
},
- init() {
+ async init() {
this.fetching = true;
- this.makePromise().then(x => {
+ await (this.makePromise()).then(x => {
if (Array.isArray(x)) {
this.notes = x;
} else {
@@ -123,10 +123,10 @@ export default Vue.extend({
});
},
- fetchMore() {
+ async fetchMore() {
if (!this.more || this.moreFetching || this.notes.length === 0) return;
this.moreFetching = true;
- this.makePromise(this.notes[this.notes.length - 1].id).then(x => {
+ await (this.makePromise(this.notes[this.notes.length - 1].id)).then(x => {
this.notes = this.notes.concat(x.notes);
this.more = x.more;
this.moreFetching = false;
diff --git a/src/client/app/mobile/views/pages/search.vue b/src/client/app/mobile/views/pages/search.vue
index 0225dd9e9f..45f3837907 100644
--- a/src/client/app/mobile/views/pages/search.vue
+++ b/src/client/app/mobile/views/pages/search.vue
@@ -12,6 +12,7 @@
import Vue from 'vue';
import i18n from '../../../i18n';
import Progress from '../../../common/scripts/loading';
+import { genSearchQuery } from '../../../common/scripts/gen-search-query';
const limit = 20;
@@ -19,10 +20,10 @@ export default Vue.extend({
i18n: i18n('mobile/views/pages/search.vue'),
data() {
return {
- makePromise: cursor => this.$root.api('notes/search', {
+ makePromise: async cursor => this.$root.api('notes/search', {
limit: limit + 1,
untilId: cursor ? cursor : undefined,
- query: this.q
+ ...(await genSearchQuery(this, this.q))
}).then(notes => {
if (notes.length == limit + 1) {
notes.pop();
diff --git a/src/db/elasticsearch.ts b/src/db/elasticsearch.ts
index d54b01763b..02c9e88d9c 100644
--- a/src/db/elasticsearch.ts
+++ b/src/db/elasticsearch.ts
@@ -1,41 +1,30 @@
-import * as elasticsearch from 'elasticsearch';
+import * as elasticsearch from '@elastic/elasticsearch';
import config from '../config';
-import Logger from '../services/logger';
-
-const esLogger = new Logger('es');
const index = {
settings: {
analysis: {
- normalizer: {
- lowercase_normalizer: {
- type: 'custom',
- filter: ['lowercase']
- }
- },
analyzer: {
- bigram: {
- tokenizer: 'bigram_tokenizer'
- }
- },
- tokenizer: {
- bigram_tokenizer: {
- type: 'nGram',
- min_gram: 2,
- max_gram: 2
+ ngram: {
+ tokenizer: 'ngram'
}
}
}
},
mappings: {
- note: {
- properties: {
- text: {
- type: 'text',
- index: true,
- analyzer: 'bigram',
- normalizer: 'lowercase_normalizer'
- }
+ properties: {
+ text: {
+ type: 'text',
+ index: true,
+ analyzer: 'ngram',
+ },
+ userId: {
+ type: 'keyword',
+ index: true,
+ },
+ userHost: {
+ type: 'keyword',
+ index: true,
}
}
}
@@ -43,31 +32,20 @@ const index = {
// Init ElasticSearch connection
const client = config.elasticsearch ? new elasticsearch.Client({
- host: `${config.elasticsearch.host}:${config.elasticsearch.port}`
+ node: `http://${config.elasticsearch.host}:${config.elasticsearch.port}`,
+ pingTimeout: 30000
}) : null;
if (client) {
- // Send a HEAD request
- client.ping({
- // Ping usually has a 3000ms timeout
- requestTimeout: 30000
- }, error => {
- if (error) {
- esLogger.error('elasticsearch is down!');
- } else {
- esLogger.succ('elasticsearch is available!');
- }
- });
-
client.indices.exists({
- index: 'misskey'
+ index: 'misskey_note'
}).then(exist => {
- if (exist) return;
-
- client.indices.create({
- index: 'misskey',
- body: index
- });
+ if (!exist.body) {
+ client.indices.create({
+ index: 'misskey_note',
+ body: index
+ });
+ }
});
}
diff --git a/src/server/api/endpoints/notes/search.ts b/src/server/api/endpoints/notes/search.ts
index daf992b639..65ce20074a 100644
--- a/src/server/api/endpoints/notes/search.ts
+++ b/src/server/api/endpoints/notes/search.ts
@@ -5,6 +5,7 @@ import { ApiError } from '../../error';
import { Notes } from '../../../../models';
import { In } from 'typeorm';
import { types, bool } from '../../../../misc/schema';
+import { ID } from '../../../../misc/cafy-id';
export const meta = {
desc: {
@@ -29,7 +30,17 @@ export const meta = {
offset: {
validator: $.optional.num.min(0),
default: 0
- }
+ },
+
+ host: {
+ validator: $.optional.nullable.str,
+ default: undefined
+ },
+
+ userId: {
+ validator: $.optional.nullable.type(ID),
+ default: null
+ },
},
res: {
@@ -54,30 +65,51 @@ export const meta = {
export default define(meta, async (ps, me) => {
if (es == null) throw new ApiError(meta.errors.searchingNotAvailable);
- const response = await es.search({
- index: 'misskey',
- type: 'note',
+ const userQuery = ps.userId != null ? [{
+ term: {
+ userId: ps.userId
+ }
+ }] : [];
+
+ const hostQuery = ps.userId == null ?
+ ps.host === null ? [{
+ bool: {
+ must_not: {
+ exists: {
+ field: 'userHost'
+ }
+ }
+ }
+ }] : ps.host !== undefined ? [{
+ term: {
+ userHost: ps.host
+ }
+ }] : []
+ : [];
+
+ const result = await es.search({
+ index: 'misskey_note',
body: {
size: ps.limit!,
from: ps.offset,
query: {
- simple_query_string: {
- fields: ['text'],
- query: ps.query,
- default_operator: 'and'
+ bool: {
+ must: [{
+ simple_query_string: {
+ fields: ['text'],
+ query: ps.query.toLowerCase(),
+ default_operator: 'and'
+ },
+ }, ...hostQuery, ...userQuery]
}
},
- sort: [
- { _doc: 'desc' }
- ]
+ sort: [{
+ _doc: 'desc'
+ }]
}
});
- if (response.hits.total === 0) {
- return [];
- }
-
- const hits = response.hits.hits.map((hit: any) => hit.id);
+ const hits = result.body.hits.hits.map((hit: any) => hit._id);
if (hits.length === 0) return [];
diff --git a/src/services/note/create.ts b/src/services/note/create.ts
index 53e77b4ef2..dd47632caa 100644
--- a/src/services/note/create.ts
+++ b/src/services/note/create.ts
@@ -435,11 +435,12 @@ function index(note: Note) {
if (note.text == null || config.elasticsearch == null) return;
es!.index({
- index: 'misskey',
- type: 'note',
+ index: 'misskey_note',
id: note.id.toString(),
body: {
- text: note.text
+ text: note.text.toLowerCase(),
+ userId: note.userId,
+ userHost: note.userHost
}
});
}