Skip to content

Commit efe2950

Browse files
authored
Merge pull request #36 from Eroxl/search
[Feature] Searching a users note rack
2 parents a54634c + 213a54c commit efe2950

File tree

26 files changed

+762
-75
lines changed

26 files changed

+762
-75
lines changed

backend/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
"devDependencies": {
88
"@types/cors": "^2.8.12",
99
"@types/express": "^4.17.13",
10-
"@types/express-ws": "^3.0.1",
1110
"@types/redis": "^4.0.11",
1211
"@types/swagger-jsdoc": "^6.0.1",
1312
"@types/swagger-ui-express": "^4.1.3",
@@ -32,6 +31,7 @@
3231
"author": "",
3332
"license": "gpl-3.0",
3433
"dependencies": {
34+
"@elastic/elasticsearch": "^8.6.0",
3535
"@types/bcrypt": "^5.0.0",
3636
"@types/cookie-parser": "^1.4.2",
3737
"@types/ioredis": "^4.28.8",
@@ -43,7 +43,6 @@
4343
"cors": "^2.8.5",
4444
"dotenv": "^14.2.0",
4545
"express": "^4.17.2",
46-
"express-ws": "^5.0.2",
4746
"ioredis": "^4.28.5",
4847
"jsonwebtoken": "^8.5.1",
4948
"mongoose": "^6.1.7",

backend/src/helpers/deletePage.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import PageMapModel from '../models/pageMap';
33
import PageModel from '../models/pageModel';
44
import PageTreeModel from '../models/pageTreeModel';
55
import mongoose from 'mongoose';
6+
import ElasticSearchClient from './search/ElasticSearchClient';
67

78
interface pageTreeType {
89
_id: string,
@@ -43,8 +44,8 @@ const deletePage = async (
4344
return subPages;
4445
};
4546

46-
const pagesToDelete = getSubPages(pageTree, pageMap.pathToPage, pageID)
47-
.map((id) => new mongoose.Types.ObjectId(id));
47+
const pagesToDelete = getSubPages(pageTree, pageMap.pathToPage, pageID);
48+
const objectIDsToDelete = pagesToDelete.map((id) => new mongoose.Types.ObjectId(id));
4849

4950
// -=- Delete Sub Pages -=-
5051
// ~ Delete the top level page from the page tree
@@ -90,14 +91,33 @@ const deletePage = async (
9091
// ~ Delete the sub pages maps
9192
await PageMapModel.deleteMany({
9293
_id: {
93-
$in: pagesToDelete,
94+
$in: objectIDsToDelete,
9495
},
9596
});
9697

9798
// ~ Delete the sub pages
9899
await PageModel.deleteMany({
99100
_id: {
100-
$in: pagesToDelete,
101+
$in: objectIDsToDelete,
102+
},
103+
});
104+
105+
106+
// ~ Delete the sub pages from ElasticSearch
107+
await ElasticSearchClient.deleteByQuery({
108+
index: 'blocks',
109+
body: {
110+
query: {
111+
bool: {
112+
filter: [
113+
{
114+
terms: {
115+
pageId: pagesToDelete,
116+
},
117+
},
118+
],
119+
},
120+
},
101121
},
102122
});
103123
};
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { Client } from '@elastic/elasticsearch';
2+
3+
if (!process.env.ELASTICSEARCH_URL) {
4+
throw new Error('ELASTICSEARCH_URL is not defined');
5+
}
6+
7+
if (!process.env.ELASTICSEARCH_PASSWORD) {
8+
throw new Error('ELASTICSEARCH_PASSWORD is not defined');
9+
}
10+
11+
if (!process.env.ELASTICSEARCH_USERNAME) {
12+
throw new Error('ELASTICSEARCH_USERNAME is not defined');
13+
}
14+
15+
const client = new Client({
16+
node: process.env.ELASTICSEARCH_URL,
17+
auth: {
18+
password: process.env.ELASTICSEARCH_PASSWORD,
19+
username: process.env.ELASTICSEARCH_USERNAME,
20+
},
21+
});
22+
23+
client.indices.exists({
24+
index: 'blocks',
25+
})
26+
.then((exists) => {
27+
if (exists) return;
28+
29+
return client.indices.create({
30+
index: 'blocks',
31+
});
32+
})
33+
.then(() => {
34+
return client.indices.putMapping({
35+
index: 'blocks',
36+
body: {
37+
properties: {
38+
blockId: {
39+
type: 'keyword',
40+
},
41+
content: {
42+
type: 'text',
43+
},
44+
pageId: {
45+
type: 'keyword',
46+
},
47+
userID: {
48+
type: 'keyword',
49+
},
50+
},
51+
},
52+
});
53+
})
54+
.catch((err) => {
55+
if (err?.meta?.body?.error?.type !== 'resource_already_exists_exception') {
56+
console.error(err);
57+
}
58+
});
59+
60+
export default client;

backend/src/index.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import supertokens from 'supertokens-node';
33
import swaggerJSDoc from 'swagger-jsdoc';
44
import swaggerUI from 'swagger-ui-express';
55
import express from 'express';
6-
import expressWs from 'express-ws';
76
import bodyParser from 'body-parser';
87
import mongoose from 'mongoose';
98
import dotenv from 'dotenv';
@@ -44,9 +43,6 @@ app.use(cors({
4443
// -=- Add Super Tokens Middleware -=-
4544
app.use(middleware());
4645

47-
// -=- Add Websocket Support -=-
48-
expressWs(app);
49-
5046
import routes from './routes/index';
5147

5248
// -=- Add API Routes -=-
@@ -78,5 +74,7 @@ app.use(
7874
// -=- Setup Super Tokens Error Handling -=-
7975
app.use(errorHandler());
8076

77+
console.log(`Server running on port ${port}`);
78+
8179
// -=- Start The Express Server -=-
8280
app.listen(port);

backend/src/middleware/verifyPermissions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const verifyPermissions = (permissions: ValidPermissions[]) => {
2727

2828
const username = req.session?.getUserId() || '';
2929

30-
const pageData = await PageModel.findOne({ _id: page }).lean();
30+
const pageData = await PageModel.findOne({ _id: page });
3131

3232
if (!pageData) {
3333
res.statusCode = 404;

backend/src/models/pageModel.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1-
import mongoose, { Schema } from 'mongoose';
1+
import mongoose, { Schema, Document } from 'mongoose';
22

3-
export interface IPage {
3+
interface Block {
4+
blockType: string;
5+
properties: {};
6+
children: Block[];
7+
}
8+
9+
export interface IPage extends Document {
410
user: string;
511
permissions: {
612
[key: string]: {
@@ -11,11 +17,7 @@ export interface IPage {
1117
};
1218
};
1319
style: {};
14-
data: {
15-
blockType: string;
16-
properties: {};
17-
children: [];
18-
}[];
20+
data: Block[];
1921
}
2022

2123
const PageSchema = new Schema<IPage>({
@@ -31,6 +33,6 @@ const PageSchema = new Schema<IPage>({
3133
],
3234
});
3335

34-
const PageModel = mongoose.models.page as mongoose.Model<IPage> || mongoose.model<IPage>('page', PageSchema);
36+
const PageModel = mongoose.models.page as mongoose.Model<IPage> || mongoose.model('page', PageSchema);
3537

3638
export default PageModel;

backend/src/routes/account/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import express from 'express';
22

33
import editPageTreeExpansion from './editPageTreeExpansion';
44
import getPageTree from './getPageTree';
5+
import search from './search';
56

67
const router = express.Router();
78

@@ -17,4 +18,10 @@ router.use(
1718
editPageTreeExpansion,
1819
);
1920

21+
// -=- Create Search API -=-
22+
router.use(
23+
'/',
24+
search,
25+
);
26+
2027
export default router;
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import express from 'express';
2+
import { SessionRequest } from 'supertokens-node/framework/express';
3+
import { verifySession } from 'supertokens-node/recipe/session/framework/express';
4+
import ElasticSearchClient from '../../helpers/search/ElasticSearchClient';
5+
6+
const router = express.Router();
7+
8+
router.get(
9+
'/search',
10+
verifySession(),
11+
async (req: SessionRequest, res) => {
12+
const username = req.session!.getUserId();
13+
const filter = req.query.filter;
14+
15+
if (typeof filter !== 'string') {
16+
res.statusCode = 401;
17+
res.json({
18+
status: 'error',
19+
message: 'Please enter a search term!',
20+
});
21+
return;
22+
}
23+
24+
// const results = await ElasticSearchClient.search({
25+
// index: 'blocks',
26+
// query: {
27+
// bool: {
28+
// must: {
29+
// match: {
30+
// content: filter,
31+
// }
32+
// },
33+
// filter: {
34+
// term: {
35+
// userID: username,
36+
// }
37+
// }
38+
// }
39+
// },
40+
// });
41+
42+
// console.log(JSON.stringify(
43+
// results,
44+
// null,
45+
// 2,
46+
// ))
47+
48+
const results = await ElasticSearchClient.search({
49+
index: 'blocks',
50+
query: {
51+
bool: {
52+
must: {
53+
match: {
54+
content: filter,
55+
},
56+
},
57+
filter: {
58+
term: {
59+
userID: username,
60+
},
61+
},
62+
}
63+
},
64+
});
65+
66+
res.statusCode = 200;
67+
res.json({
68+
status: 'success',
69+
message: results.hits.hits.map((hit) => {
70+
const sources = (hit?._source as Record<string, unknown>) ?? {};
71+
72+
return ({
73+
content: sources?.content || '',
74+
blockID: sources?.blockId || '',
75+
pageID: sources?.pageId || '',
76+
})
77+
})
78+
});
79+
},
80+
);
81+
82+
export default router;

0 commit comments

Comments
 (0)