Skip to content

Commit

Permalink
Merge pull request #716 from hiro0218/develop
Browse files Browse the repository at this point in the history
develop
  • Loading branch information
hiro0218 committed May 7, 2023
2 parents da8a399 + fec4189 commit 8542c9b
Show file tree
Hide file tree
Showing 22 changed files with 397 additions and 286 deletions.
61 changes: 21 additions & 40 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 5 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
"private": true,
"scripts": {
"dev": "next dev -p 8080",
"prebuild": "git submodule update --remote && npm run build:article && npm run build:tag-similarity && npm run build:feed",
"prebuild": "git submodule update --remote && npm run build:article && npm run build:similarity && npm run build:feed",
"build": "next build",
"postbuild": "npm run build:sitemap && npm run build:ogp",
"build:article": "node --require esbuild-register ./src/build/article",
"build:feed": "node --require esbuild-register ./src/build/feed.ts",
"build:ogp": "node --require esbuild-register ./src/build/ogp && node --require esbuild-register ./src/build/ogp/convertPngToWebP.ts",
"build:sitemap": "node --require esbuild-register node_modules/next-sitemap/bin/next-sitemap",
"build:tag-similarity": "node --require esbuild-register ./src/build/tag-similarity",
"build:similarity": "node --require esbuild-register ./src/build/similarity",
"build:analyzer": "ANALYZE=true next build",
"start": "next start",
"lint": "eslint \"src/**/*{.js,.ts,.tsx}\"",
Expand Down Expand Up @@ -60,10 +60,9 @@
"@types/fs-extra": "^11.0.1",
"@types/gtag.js": "^0.0.12",
"@types/hast": "^2.3.4",
"@types/node": "^20.0.0",
"@types/react": "^18.2.5",
"@types/react-dom": "^18.2.3",
"@types/sharp": "^0.31.1",
"@types/node": "^20.1.0",
"@types/react": "^18.2.6",
"@types/react-dom": "^18.2.4",
"@types/twitter-for-web": "^0.0.2",
"@typescript-eslint/eslint-plugin": "^5.59.1",
"budoux": "^0.5.1",
Expand Down
25 changes: 25 additions & 0 deletions src/build/similarity/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { writeJSONSync } from 'fs-extra';

import { FILENAME_POSTS_SIMILARITY, FILENAME_TAG_SIMILARITY } from '@/constant';
import * as Log from '@/lib/Log';
import { getPostsListJson, getTagsJson } from '@/lib/posts';

import { getRelatedPosts } from './post';
import { getRelatedTags } from './tag';

const posts = getPostsListJson();
const tags = getTagsJson();

const PATH_DIST = `${process.cwd()}/dist`;

// 関連タグを計算する
const relatedTags = getRelatedTags(posts, tags);

writeJSONSync(`${PATH_DIST}/${FILENAME_TAG_SIMILARITY}.json`, relatedTags);
Log.info(`Write dist/${FILENAME_TAG_SIMILARITY}.json`);

// 関連記事を計算する
const relatedPosts = getRelatedPosts(posts, posts, relatedTags);

writeJSONSync(`${PATH_DIST}/${FILENAME_POSTS_SIMILARITY}.json`, relatedPosts);
Log.info(`Write dist/${FILENAME_POSTS_SIMILARITY}.json`);
51 changes: 51 additions & 0 deletions src/build/similarity/post.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Post as PostProps, TagSimilar } from '@/types/source';

function calculatePostSimilarity(post: Partial<PostProps>, targetPostTags: PostProps['tags'], sortedTags: TagSimilar) {
let similarityScore = 0;

for (let i = 0; i < targetPostTags.length; i++) {
const tag = targetPostTags[i];
if (sortedTags[tag]) {
for (let j = 0; j < post.tags.length; j++) {
const relatedTag = post.tags[j];
if (sortedTags[tag][relatedTag]) {
similarityScore += sortedTags[tag][relatedTag];
}
}
}
}

return Number(similarityScore.toFixed(4));
}

export function getRelatedPosts(
targetPosts: Partial<PostProps>[],
posts: Partial<PostProps>[],
sortedTags: TagSimilar,
) {
const LIMIT = 6;
return targetPosts.map((targetPost) => {
const targetPostTags = targetPost.tags;
const scoredArticles = [];

posts.forEach((post) => {
if (post.slug !== targetPost.slug) {
const similarityScore = calculatePostSimilarity(post, targetPostTags, sortedTags);
if (similarityScore > 0) {
scoredArticles.push({ ...post, similarityScore });
}
}
});

scoredArticles.sort((a, b) => b.similarityScore - a.similarityScore);

const relatedArticles = scoredArticles.slice(0, LIMIT).reduce((acc, post) => {
acc[post.slug] = post.similarityScore;
return acc;
}, {});

return {
[targetPost.slug]: relatedArticles,
};
});
}
92 changes: 92 additions & 0 deletions src/build/similarity/tag.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { Post as PostProps, TagSimilar, TagsList } from '@/types/source';

export type OccurrenceMatrixProps = {
[tag: string]: {
[relatedTag: string]: number;
};
};

function createCoOccurrenceMatrix(posts: Partial<PostProps>[]) {
const coOccurrenceMatrix: OccurrenceMatrixProps = {};

for (let i = 0; i < posts.length; i++) {
const tags = posts[i].tags;
for (let j = 0; j < tags.length; j++) {
const tag = tags[j];
if (!coOccurrenceMatrix[tag]) {
coOccurrenceMatrix[tag] = {};
}
for (let k = 0; k < tags.length; k++) {
const otherTag = tags[k];
if (j !== k) {
if (!coOccurrenceMatrix[tag][otherTag]) {
coOccurrenceMatrix[tag][otherTag] = 1;
} else {
coOccurrenceMatrix[tag][otherTag]++;
}
}
}
}
}

return coOccurrenceMatrix;
}

function calculateTagRelations(tags: TagsList, coOccurrenceMatrix: OccurrenceMatrixProps) {
const tagRelations: TagSimilar = {};
const tagsKeys = Object.keys(tags);

for (let i = 0, tagKeysLen = tagsKeys.length; i < tagKeysLen; i++) {
const tag = tagsKeys[i];
const tagArticles = tags[tag];
const tagArticleCount = tagArticles.length;
const coOccurrenceTags = coOccurrenceMatrix[tag];
const coOccurrenceTagKeys = Object.keys(coOccurrenceTags);

for (let j = 0, coOccurrenceKeysLen = coOccurrenceTagKeys.length; j < coOccurrenceKeysLen; j++) {
const otherTag = coOccurrenceTagKeys[j];
const otherTagArticles = tags[otherTag];
if (!otherTagArticles) {
continue;
}
const otherTagArticleCount = otherTagArticles.length;
const count = coOccurrenceTags[otherTag];
const relation = count / Math.sqrt(tagArticleCount * otherTagArticleCount);
if (!tagRelations[tag]) {
tagRelations[tag] = {};
}
tagRelations[tag][otherTag] = relation;
}
}

return tagRelations;
}

function sortTagRelations(tagRelations: TagSimilar) {
const sortedTags: TagSimilar = {};
const tagRelationsKeys = Object.keys(tagRelations);

for (let i = 0, tagRelationsKeysLen = tagRelationsKeys.length; i < tagRelationsKeysLen; i++) {
const tag = tagRelationsKeys[i];
const sortedRelatedTags = {};
const relatedTagEntries = Object.entries(tagRelations[tag]);
relatedTagEntries.sort((a, b) => b[1] - a[1]);

for (let j = 0, relatedTagEntriesLen = relatedTagEntries.length; j < relatedTagEntriesLen; j++) {
const [key, value] = relatedTagEntries[j];
sortedRelatedTags[key] = Number(value.toFixed(4));
}

sortedTags[tag] = sortedRelatedTags;
}

return sortedTags;
}

export function getRelatedTags(posts: Partial<PostProps>[], tags: TagsList) {
const coOccurrenceMatrix = createCoOccurrenceMatrix(posts);
const tagRelations = calculateTagRelations(tags, coOccurrenceMatrix);
const sortedTags = sortTagRelations(tagRelations);

return sortedTags;
}
Loading

1 comment on commit 8542c9b

@vercel
Copy link

@vercel vercel bot commented on 8542c9b May 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

b-0218-jp – ./

b-0218-jp-git-master-hiro.vercel.app
b-0218-jp-hiro.vercel.app
b.0218.jp

Please sign in to comment.