Merge branch 'search-improves'
This commit is contained in:
@ -5,7 +5,7 @@ import keydown from 'react-keydown';
|
|||||||
import Waypoint from 'react-waypoint';
|
import Waypoint from 'react-waypoint';
|
||||||
import { observable, action } from 'mobx';
|
import { observable, action } from 'mobx';
|
||||||
import { observer, inject } from 'mobx-react';
|
import { observer, inject } from 'mobx-react';
|
||||||
import { SearchResult } from 'types';
|
import type { SearchResult } from 'types';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import DocumentsStore, {
|
import DocumentsStore, {
|
||||||
DEFAULT_PAGINATION_LIMIT,
|
DEFAULT_PAGINATION_LIMIT,
|
||||||
|
@ -3,7 +3,7 @@ import TestServer from 'fetch-test-server';
|
|||||||
import app from '..';
|
import app from '..';
|
||||||
import { Document, View, Star, Revision } from '../models';
|
import { Document, View, Star, Revision } from '../models';
|
||||||
import { flushdb, seed } from '../test/support';
|
import { flushdb, seed } from '../test/support';
|
||||||
import { buildShare, buildUser } from '../test/factories';
|
import { buildShare, buildUser, buildDocument } from '../test/factories';
|
||||||
|
|
||||||
const server = new TestServer(app.callback());
|
const server = new TestServer(app.callback());
|
||||||
|
|
||||||
@ -231,6 +231,68 @@ describe('#documents.search', async () => {
|
|||||||
expect(body.data[0].document.text).toEqual('# Much guidance');
|
expect(body.data[0].document.text).toEqual('# Much guidance');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return results in ranked order', async () => {
|
||||||
|
const { user } = await seed();
|
||||||
|
const firstResult = await buildDocument({
|
||||||
|
title: 'search term',
|
||||||
|
text: 'random text',
|
||||||
|
userId: user.id,
|
||||||
|
teamId: user.teamId,
|
||||||
|
});
|
||||||
|
const secondResult = await buildDocument({
|
||||||
|
title: 'random text',
|
||||||
|
text: 'search term',
|
||||||
|
userId: user.id,
|
||||||
|
teamId: user.teamId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const res = await server.post('/api/documents.search', {
|
||||||
|
body: { token: user.getJwtToken(), query: 'search term' },
|
||||||
|
});
|
||||||
|
const body = await res.json();
|
||||||
|
|
||||||
|
expect(res.status).toEqual(200);
|
||||||
|
expect(body.data.length).toEqual(2);
|
||||||
|
expect(body.data[0].document.id).toEqual(firstResult.id);
|
||||||
|
expect(body.data[1].document.id).toEqual(secondResult.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return draft documents created by user', async () => {
|
||||||
|
const { user } = await seed();
|
||||||
|
await buildDocument({
|
||||||
|
title: 'search term',
|
||||||
|
text: 'search term',
|
||||||
|
publishedAt: null,
|
||||||
|
userId: user.id,
|
||||||
|
teamId: user.teamId,
|
||||||
|
});
|
||||||
|
const res = await server.post('/api/documents.search', {
|
||||||
|
body: { token: user.getJwtToken(), query: 'search term' },
|
||||||
|
});
|
||||||
|
const body = await res.json();
|
||||||
|
|
||||||
|
expect(res.status).toEqual(200);
|
||||||
|
expect(body.data.length).toEqual(1);
|
||||||
|
expect(body.data[0].document.text).toEqual('search term');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not return draft documents created by other users', async () => {
|
||||||
|
const { user } = await seed();
|
||||||
|
await buildDocument({
|
||||||
|
title: 'search term',
|
||||||
|
text: 'search term',
|
||||||
|
publishedAt: null,
|
||||||
|
teamId: user.teamId,
|
||||||
|
});
|
||||||
|
const res = await server.post('/api/documents.search', {
|
||||||
|
body: { token: user.getJwtToken(), query: 'search term' },
|
||||||
|
});
|
||||||
|
const body = await res.json();
|
||||||
|
|
||||||
|
expect(res.status).toEqual(200);
|
||||||
|
expect(body.data.length).toEqual(0);
|
||||||
|
});
|
||||||
|
|
||||||
it('should require authentication', async () => {
|
it('should require authentication', async () => {
|
||||||
const res = await server.post('/api/documents.search');
|
const res = await server.post('/api/documents.search');
|
||||||
const body = await res.json();
|
const body = await res.json();
|
||||||
|
@ -7,7 +7,7 @@ import Plain from 'slate-plain-serializer';
|
|||||||
import Sequelize from 'sequelize';
|
import Sequelize from 'sequelize';
|
||||||
|
|
||||||
import isUUID from 'validator/lib/isUUID';
|
import isUUID from 'validator/lib/isUUID';
|
||||||
import { Collection } from '../models';
|
import { Collection, User } from '../models';
|
||||||
import { DataTypes, sequelize } from '../sequelize';
|
import { DataTypes, sequelize } from '../sequelize';
|
||||||
import events from '../events';
|
import events from '../events';
|
||||||
import parseTitle from '../../shared/utils/parseTitle';
|
import parseTitle from '../../shared/utils/parseTitle';
|
||||||
@ -211,8 +211,11 @@ Document.searchForUser = async (
|
|||||||
FROM documents
|
FROM documents
|
||||||
WHERE "searchVector" @@ plainto_tsquery('english', :query) AND
|
WHERE "searchVector" @@ plainto_tsquery('english', :query) AND
|
||||||
"teamId" = '${user.teamId}'::uuid AND
|
"teamId" = '${user.teamId}'::uuid AND
|
||||||
"deletedAt" IS NULL
|
"deletedAt" IS NULL AND
|
||||||
ORDER BY "searchRanking", "updatedAt" DESC
|
("publishedAt" IS NOT NULL OR "createdById" = '${user.id}')
|
||||||
|
ORDER BY
|
||||||
|
"searchRanking" DESC,
|
||||||
|
"updatedAt" DESC
|
||||||
LIMIT :limit
|
LIMIT :limit
|
||||||
OFFSET :offset;
|
OFFSET :offset;
|
||||||
`;
|
`;
|
||||||
@ -227,12 +230,15 @@ Document.searchForUser = async (
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Second query to get associated document data
|
// Second query to get associated document data
|
||||||
const withViewsScope = { method: ['withViews', user.id] };
|
const documents = await Document.scope({
|
||||||
const documents = await Document.scope(
|
method: ['withViews', user.id],
|
||||||
'defaultScope',
|
}).findAll({
|
||||||
withViewsScope
|
|
||||||
).findAll({
|
|
||||||
where: { id: map(results, 'id') },
|
where: { id: map(results, 'id') },
|
||||||
|
include: [
|
||||||
|
{ model: Collection, as: 'collection' },
|
||||||
|
{ model: User, as: 'createdBy', paranoid: false },
|
||||||
|
{ model: User, as: 'updatedBy', paranoid: false },
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
return map(results, result => ({
|
return map(results, result => ({
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import { Share, Team, User } from '../models';
|
import { Share, Team, User, Document, Collection } from '../models';
|
||||||
import uuid from 'uuid';
|
import uuid from 'uuid';
|
||||||
|
|
||||||
let count = 0;
|
let count = 0;
|
||||||
@ -45,3 +45,53 @@ export async function buildUser(overrides: Object = {}) {
|
|||||||
...overrides,
|
...overrides,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function buildCollection(overrides: Object = {}) {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
if (!overrides.teamId) {
|
||||||
|
const team = await buildTeam();
|
||||||
|
overrides.teamId = team.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!overrides.userId) {
|
||||||
|
const user = await buildUser();
|
||||||
|
overrides.userId = user.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Collection.create({
|
||||||
|
name: 'Test Collection',
|
||||||
|
description: 'Test collection description',
|
||||||
|
creatorId: overrides.userId,
|
||||||
|
type: 'atlas',
|
||||||
|
...overrides,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function buildDocument(overrides: Object = {}) {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
if (!overrides.teamId) {
|
||||||
|
const team = await buildTeam();
|
||||||
|
overrides.teamId = team.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!overrides.userId) {
|
||||||
|
const user = await buildUser();
|
||||||
|
overrides.userId = user.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!overrides.atlasId) {
|
||||||
|
const collection = await buildCollection(overrides);
|
||||||
|
overrides.atlasId = collection.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Document.create({
|
||||||
|
title: `Document ${count}`,
|
||||||
|
text: 'This is the text in an example document',
|
||||||
|
publishedAt: new Date(),
|
||||||
|
lastModifiedById: overrides.userId,
|
||||||
|
createdById: overrides.userId,
|
||||||
|
...overrides,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user