-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor code and simplify file processing #48
Changes from 16 commits
c878a75
2dc1cc8
89aac13
2d55645
15e4f2e
a2d6109
532b933
8348585
3a9602d
a5c2f42
f7ef9d5
6712e30
41e1714
e2b9d19
adbe8d9
1f33626
1655f74
e56be54
b285d2d
7dc9400
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import { Knex } from "knex"; | ||
import { MddbFile, MddbTag, MddbLink } from "./schema.js"; | ||
import { FilesQuery } from "./types/DbQueryTypes.js"; | ||
|
||
export class DbQueryManager { | ||
private db: Knex; | ||
|
||
constructor(db: Knex) { | ||
this.db = db; | ||
} | ||
|
||
async getFileById(id: string): Promise<MddbFile | null> { | ||
const file = await this.db.from("files").where("_id", id).first(); | ||
return new MddbFile(file); | ||
} | ||
|
||
async getFileByUrl(url: string): Promise<MddbFile | null> { | ||
const file = await this.db | ||
.from("files") | ||
.where("url_path", encodeURI(url)) | ||
.first(); | ||
return new MddbFile(file); | ||
} | ||
|
||
async getFiles(query?: FilesQuery): Promise<MddbFile[]> { | ||
const { filetypes, tags, extensions, folder, frontmatter } = query || {}; | ||
|
||
const files = await this.db | ||
// TODO join only if tags are specified ? | ||
.leftJoin("file_tags", "files._id", "file_tags.file") | ||
.where((builder) => { | ||
// TODO temporary solution before we have a proper way to filter files by and assign file types | ||
if (folder) { | ||
builder.whereLike("url_path", `${folder}/%`); | ||
} | ||
|
||
if (tags) { | ||
builder.whereIn("tag", tags); | ||
} | ||
|
||
if (extensions) { | ||
builder.whereIn("extension", extensions); | ||
} | ||
|
||
if (filetypes) { | ||
builder.whereIn("filetype", filetypes); | ||
} | ||
|
||
if (frontmatter) { | ||
Object.entries(frontmatter).forEach(([key, value]) => { | ||
if (typeof value === "string" || typeof value === "number") { | ||
builder.whereRaw(`json_extract(metadata, '$.${key}') = ?`, [ | ||
value, | ||
]); | ||
} else if (typeof value === "boolean") { | ||
if (value) { | ||
builder.whereRaw(`json_extract(metadata, '$.${key}') = ?`, [ | ||
true, | ||
]); | ||
} else { | ||
builder.where(function () { | ||
this.whereRaw(`json_extract(metadata, '$.${key}') = ?`, [ | ||
false, | ||
]).orWhereRaw(`json_extract(metadata, '$.${key}') IS NULL`); | ||
}); | ||
} | ||
} | ||
|
||
// To check if the provided value exists in an array inside the JSON | ||
else { | ||
builder.whereRaw(`json_extract(metadata, '$.${key}') LIKE ?`, [ | ||
`%${value}%`, | ||
]); | ||
} | ||
}); | ||
} | ||
}) | ||
.select("files.*") | ||
.from("files") | ||
.groupBy("_id"); | ||
|
||
return files.map((file) => new MddbFile(file)); | ||
} | ||
|
||
async getTags(): Promise<MddbTag[]> { | ||
const tags = await this.db("tags").select(); | ||
return tags.map((tag) => new MddbTag(tag)); | ||
} | ||
|
||
async getLinks(query?: { | ||
fileId: string; | ||
linkType?: "normal" | "embed"; | ||
direction?: "forward" | "backward"; | ||
}): Promise<MddbLink[]> { | ||
const { fileId, direction = "forward", linkType } = query || {}; | ||
const joinKey = direction === "forward" ? "from" : "to"; | ||
const where = { | ||
[joinKey]: fileId, | ||
}; | ||
if (linkType) { | ||
where["link_type"] = linkType; | ||
} | ||
const dbLinks = await this.db | ||
.select("links.*") | ||
.from("links") | ||
.rightJoin("files", `links.${joinKey}`, "=", "files._id") | ||
.where(where); | ||
|
||
const links = dbLinks.map((link) => new MddbLink(link)); | ||
return links; | ||
} | ||
|
||
_destroyDb() { | ||
this.db.destroy(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,79 @@ | ||||||||||||||||||||||||||||||||||||||
import { getUniqueValues, recursiveWalkDir } from "../utils/index.js"; | ||||||||||||||||||||||||||||||||||||||
import type { WikiLink } from "../utils/index.js"; | ||||||||||||||||||||||||||||||||||||||
import { extractFileSchemeFromObject } from "../utils/extractFileSchemeFromObject.js"; | ||||||||||||||||||||||||||||||||||||||
import { readLocalMarkdownFileToObject } from "./readLocalMarkdownFileToObject.js"; | ||||||||||||||||||||||||||||||||||||||
import type { File, FileTag, Link } from "./types/schemaTypes.js"; | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
export function indexFolderToObjects( | ||||||||||||||||||||||||||||||||||||||
folderPath: string, | ||||||||||||||||||||||||||||||||||||||
pathToUrlResolver: (filePath: string) => string, | ||||||||||||||||||||||||||||||||||||||
ignorePatterns?: RegExp[] | ||||||||||||||||||||||||||||||||||||||
) { | ||||||||||||||||||||||||||||||||||||||
const filePathsToIndex = recursiveWalkDir(folderPath); | ||||||||||||||||||||||||||||||||||||||
const files: File[] = []; | ||||||||||||||||||||||||||||||||||||||
const tags: string[] = []; | ||||||||||||||||||||||||||||||||||||||
const fileTags: FileTag[] = []; | ||||||||||||||||||||||||||||||||||||||
const links: Link[] = []; | ||||||||||||||||||||||||||||||||||||||
const filteredFilePathsToIndex = filePathsToIndex.filter((filePath) => | ||||||||||||||||||||||||||||||||||||||
shouldIncludeFile(filePath, ignorePatterns) | ||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
for (const filePath of filteredFilePathsToIndex) { | ||||||||||||||||||||||||||||||||||||||
const fileObject = readLocalMarkdownFileToObject( | ||||||||||||||||||||||||||||||||||||||
folderPath, | ||||||||||||||||||||||||||||||||||||||
filePath, | ||||||||||||||||||||||||||||||||||||||
filePathsToIndex, | ||||||||||||||||||||||||||||||||||||||
pathToUrlResolver | ||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks a bit awkward imo... Both the naming, the arguments it takes and unnecesarily adds a layer of abstraction. I wonder if we could get rid of the
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I agree. |
||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
const file = extractFileSchemeFromObject(fileObject); | ||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd suggest "marshalling" (or molding the object into the ready-to-insert-to-db structure) is done by relevant Mddb* classes (in this case MddbFile). So I'd just add the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm ready to proceed, but I have a question about the structure of the data in the JSON files. Do you think it should mirror the format of the database? If they should align, we might want to keep things as they are. However, if variations are acceptable, I am more than willing to implement the modifications based on your recommended alternative structure. |
||||||||||||||||||||||||||||||||||||||
files.push(file); | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
tags.push(...fileObject.tags); | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
const fileTagsToInsert = fileObject.tags.map((tag) => ({ | ||||||||||||||||||||||||||||||||||||||
tag: tag, | ||||||||||||||||||||||||||||||||||||||
file: fileObject._id, | ||||||||||||||||||||||||||||||||||||||
})); | ||||||||||||||||||||||||||||||||||||||
fileTags.push(...fileTagsToInsert); | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
const linksToInsert: Link[] = processWikiLinks( | ||||||||||||||||||||||||||||||||||||||
fileObject.links, | ||||||||||||||||||||||||||||||||||||||
fileObject._id, | ||||||||||||||||||||||||||||||||||||||
filePathsToIndex | ||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||
links.push(...linksToInsert); | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
const uniqueTags = getUniqueValues(tags); | ||||||||||||||||||||||||||||||||||||||
const TagsToInsert = uniqueTags.map((tag) => ({ name: tag })); | ||||||||||||||||||||||||||||||||||||||
return { | ||||||||||||||||||||||||||||||||||||||
files: files, | ||||||||||||||||||||||||||||||||||||||
tags: TagsToInsert, | ||||||||||||||||||||||||||||||||||||||
fileTags: fileTags, | ||||||||||||||||||||||||||||||||||||||
links: links, | ||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
function processWikiLinks( | ||||||||||||||||||||||||||||||||||||||
links: WikiLink[], | ||||||||||||||||||||||||||||||||||||||
fileId: string, | ||||||||||||||||||||||||||||||||||||||
filePathsToIndex: string[] | ||||||||||||||||||||||||||||||||||||||
): Link[] { | ||||||||||||||||||||||||||||||||||||||
return links | ||||||||||||||||||||||||||||||||||||||
.map((link) => ({ | ||||||||||||||||||||||||||||||||||||||
from: fileId, | ||||||||||||||||||||||||||||||||||||||
to: filePathsToIndex.find((file) => file === link.linkSrc)!, | ||||||||||||||||||||||||||||||||||||||
link_type: link.linkType, | ||||||||||||||||||||||||||||||||||||||
})) | ||||||||||||||||||||||||||||||||||||||
.filter((link) => link.to !== undefined); | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
function shouldIncludeFile( | ||||||||||||||||||||||||||||||||||||||
filePath: string, | ||||||||||||||||||||||||||||||||||||||
ignorePatterns?: RegExp[] | ||||||||||||||||||||||||||||||||||||||
): boolean { | ||||||||||||||||||||||||||||||||||||||
return !( | ||||||||||||||||||||||||||||||||||||||
ignorePatterns && ignorePatterns.some((pattern) => pattern.test(filePath)) | ||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd recommend avoiding refactoring the DB here - let's just focus on
process.ts
as discussed in the issue.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd leave the refactoring done so far in this PR. It makes the code much easier to reason about.