Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4cc7057cea |
@@ -0,0 +1,41 @@
|
|||||||
|
// 存量回填:對「已存在的 entity records」補上對應的 embeddable base entry。
|
||||||
|
// 讀 base 既有 entity records(GET /records/by-template/entity)→ 為每筆有 gloss 的落一筆 embeddable entry
|
||||||
|
// (同 persistNodes 前向路徑的格式與冪等)。讓既有知識立刻可被 base embed backfill 嵌到。
|
||||||
|
// 鐵律:走 base API(API-as-Wall)、零 SQL、樂高法(<100 行、一檔一事、第一參數收 KbdbClient)。
|
||||||
|
|
||||||
|
import type { KbdbClient } from '../lib/kbdb-client';
|
||||||
|
import { TPL_ENTITY, ensurePluginTemplates } from '../lib/templates';
|
||||||
|
import { upsertGlossEntry, type GlossEntryOutcome } from './gloss-entry';
|
||||||
|
|
||||||
|
export type BackfillGlossResult = {
|
||||||
|
scanned: number; // 掃到的 entity record 數
|
||||||
|
created: number; // 新落的 embeddable entry 數
|
||||||
|
updated: number; // gloss 變 → 更新內容的數
|
||||||
|
unchanged: number; // 已存在且內容相同(冪等 no-op)
|
||||||
|
skipped: number; // embed=false / 空 gloss / 空 canonical → 不落
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 對既有 entity records 補 embeddable entry。冪等:重跑不造重複(同 node → 同 page_name → no-op)。
|
||||||
|
* embed 標為 'false' 的 record 略過(明確不入向量庫)。source 未知(存量)→ 留空。
|
||||||
|
*/
|
||||||
|
export async function backfillGlossEntries(
|
||||||
|
client: KbdbClient,
|
||||||
|
owner_id?: string,
|
||||||
|
): Promise<BackfillGlossResult> {
|
||||||
|
await ensurePluginTemplates(client);
|
||||||
|
const records = await client.listRecordsByTemplate(TPL_ENTITY, owner_id);
|
||||||
|
const res: BackfillGlossResult = { scanned: records.length, created: 0, updated: 0, unchanged: 0, skipped: 0 };
|
||||||
|
|
||||||
|
for (const r of records) {
|
||||||
|
const v = r.values;
|
||||||
|
if (v.embed === 'false') { res.skipped++; continue; } // 明確標不嵌 → 略過
|
||||||
|
const outcome: GlossEntryOutcome = await upsertGlossEntry(
|
||||||
|
client,
|
||||||
|
{ canonical: v.canonical ?? '', node_id: v.node_id || '', gloss: v.gloss ?? '', source: '' },
|
||||||
|
owner_id,
|
||||||
|
);
|
||||||
|
res[outcome]++; // 'created' | 'updated' | 'unchanged' | 'skipped' 都是 res 的 number 欄位
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
// node gloss → base embeddable entry。把 node 的 canonical+gloss 落成一筆 base entry,
|
||||||
|
// metadata.embed=true,供 base optional embed 模組讀標 → 嵌進 Vectorize(見 arcrun/kbdb src/embed.ts)。
|
||||||
|
// 鐵律:走 base API(API-as-Wall)、零 SQL、不綁 Vectorize(embedding 是 base 職責,graph 只落標)。
|
||||||
|
//
|
||||||
|
// 為何要這檔:node 打標存進 entity **record**(node-persist),但 base embed 只掃 **entries**.metadata_json
|
||||||
|
// .$.embed===true → 標在 record、讀在 entry 對不上,語意查不到圖內容。這裡補「同一 gloss 也落成 entry」。
|
||||||
|
//
|
||||||
|
// 冪等(base 無 upsert entry 端點):以 node 去重鍵派生確定性 page_name(base 文件化的 idempotency key),
|
||||||
|
// 先 listEntries({entry_type,page_name}) 查存 → 無則 create、內容變則 patch、同則 no-op。
|
||||||
|
// [→arcrun] 若 base 日後補「POST /entries upsert(page_name 為鍵)」,這裡可收斂成單一呼叫、免 list-then-write。
|
||||||
|
|
||||||
|
import type { KbdbClient } from '../lib/kbdb-client';
|
||||||
|
|
||||||
|
// base embed 對「內容語意」無知,只認通用 embed 旗標;entry_type 供向量 metadata 過濾/辨識這批是圖節點 gloss。
|
||||||
|
export const NODE_GLOSS_ENTRY_TYPE = 'graph_node_gloss';
|
||||||
|
|
||||||
|
export type GlossEntryInput = {
|
||||||
|
canonical: string;
|
||||||
|
node_id?: string;
|
||||||
|
gloss?: string;
|
||||||
|
source?: string; // 沿用 node 的 ingest 來源(envelope source.uri);存量回填未知則留空
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GlossEntryOutcome = 'created' | 'updated' | 'unchanged' | 'skipped';
|
||||||
|
|
||||||
|
/** node 去重鍵(同 persistNodes:id 優先、無則 canonical)→ 確定性 idempotency key。 */
|
||||||
|
function glossKey(canonical: string, node_id?: string): string {
|
||||||
|
return `gloss:${(node_id || canonical).toLowerCase().trim()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 把一個 node 的 gloss 落成 embeddable base entry(冪等)。
|
||||||
|
* - 空 gloss(或空 canonical)→ 跳過:base 對空 content 本就跳過 embed,且裸名的語意召回價值低、
|
||||||
|
* keyword 搜尋已覆蓋 → 不造無意義 entry(契約允許「空 gloss 可跳過」)。
|
||||||
|
* - content = `canonical:gloss`(名+描述一起 embed,利於語意召回)。
|
||||||
|
* - metadata_json.embed=true(base 讀此旗標)、source、node_id、canonical(回連原 node)。
|
||||||
|
*/
|
||||||
|
export async function upsertGlossEntry(
|
||||||
|
client: KbdbClient,
|
||||||
|
node: GlossEntryInput,
|
||||||
|
owner_id?: string,
|
||||||
|
): Promise<GlossEntryOutcome> {
|
||||||
|
const canonical = (node.canonical || '').trim();
|
||||||
|
const gloss = (node.gloss || '').trim();
|
||||||
|
if (!canonical || !gloss) return 'skipped';
|
||||||
|
|
||||||
|
const page_name = glossKey(canonical, node.node_id);
|
||||||
|
const content = `${canonical}:${gloss}`;
|
||||||
|
const metadata_json = JSON.stringify({
|
||||||
|
embed: true,
|
||||||
|
source: node.source || '',
|
||||||
|
node_id: node.node_id || '',
|
||||||
|
canonical,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 確定性 key 去重:同一 node 重複 ingest 不造重複 entry。
|
||||||
|
const existing = await client.listEntries({ entry_type: NODE_GLOSS_ENTRY_TYPE, page_name, owner_id });
|
||||||
|
if (existing.length > 0) {
|
||||||
|
const e = existing[0];
|
||||||
|
if (e.content === content) return 'unchanged'; // 同內容 → no-op
|
||||||
|
await client.updateEntry(e.id, { content, metadata_json }); // gloss 變 → 更新(base PATCH 觸發重嵌)
|
||||||
|
return 'updated';
|
||||||
|
}
|
||||||
|
await client.createEntry({ content, entry_type: NODE_GLOSS_ENTRY_TYPE, owner_id, page_name, metadata_json });
|
||||||
|
return 'created';
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
import type { KbdbClient } from '../lib/kbdb-client';
|
import type { KbdbClient } from '../lib/kbdb-client';
|
||||||
import { TPL_ENTITY, ensurePluginTemplates } from '../lib/templates';
|
import { TPL_ENTITY, ensurePluginTemplates } from '../lib/templates';
|
||||||
|
import { upsertGlossEntry } from './gloss-entry';
|
||||||
|
|
||||||
export type IngestNode = {
|
export type IngestNode = {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -23,6 +24,7 @@ export async function persistNodes(
|
|||||||
client: KbdbClient,
|
client: KbdbClient,
|
||||||
nodes: IngestNode[],
|
nodes: IngestNode[],
|
||||||
owner_id?: string,
|
owner_id?: string,
|
||||||
|
source?: string, // envelope source.uri,帶進 gloss entry 的 metadata.source(供 base backfill 依 source 過濾)
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (!nodes || nodes.length === 0) return;
|
if (!nodes || nodes.length === 0) return;
|
||||||
await ensurePluginTemplates(client);
|
await ensurePluginTemplates(client);
|
||||||
@@ -46,5 +48,12 @@ export async function persistNodes(
|
|||||||
},
|
},
|
||||||
owner_id,
|
owner_id,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 另落一筆 embeddable base entry(metadata.embed=true)——record 的 gloss 標 base embed 讀不到,
|
||||||
|
// 必須也落成 entry base embed 模組才掃得到(打標≠讀標的修補)。只在要 embed(embed !== false)時落;
|
||||||
|
// 空 gloss 由 upsertGlossEntry 自行跳過。冪等:同 node 重複 ingest 不造重複 entry。
|
||||||
|
if (n.embed !== false) {
|
||||||
|
await upsertGlossEntry(client, { canonical: n.name, node_id: n.id, gloss: n.gloss, source }, owner_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,8 @@ export async function ingestEnvelope(
|
|||||||
|
|
||||||
// 1b) 落地 node 層打標(embed / gloss / aliases),供 base embed 模組讀標執行 embedding。
|
// 1b) 落地 node 層打標(embed / gloss / aliases),供 base embed 模組讀標執行 embedding。
|
||||||
// graph 自己不算向量(鐵律一致)。id 作去重鍵:同一卡(同 id/檔名)只存一筆 entity,不以邊數重複。
|
// graph 自己不算向量(鐵律一致)。id 作去重鍵:同一卡(同 id/檔名)只存一筆 entity,不以邊數重複。
|
||||||
await persistNodes(client, env.nodes ?? [], owner_id);
|
// 並為每個 node 另落一筆 embeddable base entry(source.uri 帶進 metadata.source)。
|
||||||
|
await persistNodes(client, env.nodes ?? [], owner_id, env.source.uri);
|
||||||
|
|
||||||
// 2) 後翻舊批 status=deprecated(指向本批 source_uri;append 在前 → 無空窗)。
|
// 2) 後翻舊批 status=deprecated(指向本批 source_uri;append 在前 → 無空窗)。
|
||||||
for (const old of priorActive) {
|
for (const old of priorActive) {
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ export type BaseEntry = {
|
|||||||
owner_id: string | null;
|
owner_id: string | null;
|
||||||
parent_id?: string | null;
|
parent_id?: string | null;
|
||||||
page_name?: string | null;
|
page_name?: string | null;
|
||||||
|
// base entries 的通用 metadata(TEXT,JSON 字串)。base embed 模組讀 metadata_json.$.embed / $.source
|
||||||
|
// 決定要不要嵌、嵌進哪個 source 分組(見 arcrun/kbdb src/embed.ts)。插件只落標、不算向量。
|
||||||
|
metadata_json?: string | null;
|
||||||
created_at?: number;
|
created_at?: number;
|
||||||
updated_at?: number;
|
updated_at?: number;
|
||||||
};
|
};
|
||||||
@@ -28,7 +31,10 @@ export type CreateEntryInput = {
|
|||||||
entry_type: string;
|
entry_type: string;
|
||||||
owner_id?: string;
|
owner_id?: string;
|
||||||
parent_id?: string;
|
parent_id?: string;
|
||||||
|
// page_name = base 文件化的 idempotency key(entry-crud.ts:exact-match lookup)。
|
||||||
page_name?: string;
|
page_name?: string;
|
||||||
|
// metadata_json = base 既有欄位(JSON 字串)。標 { embed:true, source, ... } 供 base embed 模組讀標執行。
|
||||||
|
metadata_json?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** 基本盤 API client。所有方法 = 一個 HTTP 呼叫,零 SQL。 */
|
/** 基本盤 API client。所有方法 = 一個 HTTP 呼叫,零 SQL。 */
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
rejectPendingAlias,
|
rejectPendingAlias,
|
||||||
} from '../actions/entity-pending';
|
} from '../actions/entity-pending';
|
||||||
import { listTripletEntities } from '../actions/triplet-entities';
|
import { listTripletEntities } from '../actions/triplet-entities';
|
||||||
|
import { backfillGlossEntries } from '../actions/backfill-gloss-entries';
|
||||||
import { makeKbdbClient } from '../lib/kbdb-client';
|
import { makeKbdbClient } from '../lib/kbdb-client';
|
||||||
|
|
||||||
const entityRoutes = new OpenAPIHono<{ Bindings: Bindings }>();
|
const entityRoutes = new OpenAPIHono<{ Bindings: Bindings }>();
|
||||||
@@ -64,4 +65,13 @@ entityRoutes.post('/pending/:id/reject', async (c) => {
|
|||||||
return c.json({ success: true, action: 'rejected', newEntity });
|
return c.json({ success: true, action: 'rejected', newEntity });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// POST /backfill-gloss-entries — 存量回填:對既有 entity records 補 embeddable base entry(metadata.embed=true)。
|
||||||
|
// route 只驗參數 + 呼叫 action(樂高法:無業務邏輯)。冪等,可重複呼叫。
|
||||||
|
entityRoutes.post('/backfill-gloss-entries', async (c) => {
|
||||||
|
const body = (await c.req.json().catch(() => ({}))) as { owner_id?: string };
|
||||||
|
const owner_id = c.req.query('owner_id') || body?.owner_id || undefined;
|
||||||
|
const result = await backfillGlossEntries(makeKbdbClient(c.env), owner_id);
|
||||||
|
return c.json({ success: true, ...result });
|
||||||
|
});
|
||||||
|
|
||||||
export { entityRoutes };
|
export { entityRoutes };
|
||||||
|
|||||||
@@ -0,0 +1,120 @@
|
|||||||
|
// node gloss → embeddable base entry:走 mock KbdbClient(API-as-Wall),零 SQL、不打網路。
|
||||||
|
// 覆蓋:前向 persistNodes 多落 embeddable entry / embed=false 不落 / 空 gloss 不落 / 冪等不重複 /
|
||||||
|
// gloss 變則更新 / backfill 對既有 entity records 補 entry(含冪等、embed=false 略過)。
|
||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { persistNodes } from '../src/actions/node-persist';
|
||||||
|
import { backfillGlossEntries } from '../src/actions/backfill-gloss-entries';
|
||||||
|
import { NODE_GLOSS_ENTRY_TYPE } from '../src/actions/gloss-entry';
|
||||||
|
import { TPL_ENTITY } from '../src/lib/templates';
|
||||||
|
import { mockClient } from './mock-client';
|
||||||
|
|
||||||
|
const glossEntries = (c: ReturnType<typeof mockClient>) =>
|
||||||
|
c.listEntries({ entry_type: NODE_GLOSS_ENTRY_TYPE });
|
||||||
|
|
||||||
|
describe('persistNodes — 前向:每個 node 另落一筆 embeddable base entry', () => {
|
||||||
|
it('有 gloss 的 node → 落 entry:content=名+gloss、metadata.embed=true、帶 source/node_id', async () => {
|
||||||
|
const c = mockClient();
|
||||||
|
await persistNodes(
|
||||||
|
c,
|
||||||
|
[{ name: '系統動力學', id: 'sd.md', gloss: '用因果圖表解複雜系統的方法論' }],
|
||||||
|
'leo21c',
|
||||||
|
'github:uncle6me-web/wiki@sd.md',
|
||||||
|
);
|
||||||
|
|
||||||
|
const entries = await glossEntries(c);
|
||||||
|
expect(entries.length).toBe(1);
|
||||||
|
const e = entries[0];
|
||||||
|
expect(e.content).toBe('系統動力學:用因果圖表解複雜系統的方法論');
|
||||||
|
expect(e.page_name).toBe('gloss:sd.md');
|
||||||
|
expect(e.owner_id).toBe('leo21c');
|
||||||
|
const meta = JSON.parse(e.metadata_json!);
|
||||||
|
expect(meta.embed).toBe(true); // 關鍵:base embed 模組讀這個
|
||||||
|
expect(meta.source).toBe('github:uncle6me-web/wiki@sd.md');
|
||||||
|
expect(meta.node_id).toBe('sd.md');
|
||||||
|
expect(meta.canonical).toBe('系統動力學');
|
||||||
|
|
||||||
|
// entity record 仍照舊存在(兩者並存:record 給圖、entry 給 embed)。
|
||||||
|
const recs = await c.listRecordsByTemplate(TPL_ENTITY);
|
||||||
|
expect(recs.find((r) => r.values.canonical === '系統動力學')).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('embed=false 的 node → 不落 embeddable entry', async () => {
|
||||||
|
const c = mockClient();
|
||||||
|
await persistNodes(c, [{ name: '黃仁勳', id: '黃仁勳', gloss: 'NVIDIA 創辦人', embed: false }]);
|
||||||
|
expect((await glossEntries(c)).length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('空 gloss 的 node → 不落 embeddable entry(不造無意義空殼)', async () => {
|
||||||
|
const c = mockClient();
|
||||||
|
await persistNodes(c, [{ name: '某卡', id: 'card.md' }]);
|
||||||
|
expect((await glossEntries(c)).length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('冪等:同一 node 重複 ingest → 只一筆 embeddable entry', async () => {
|
||||||
|
const c = mockClient();
|
||||||
|
const nodes = [{ name: '長壽', id: 'longevity', gloss: '活得更久本身具有價值' }];
|
||||||
|
await persistNodes(c, nodes, 'leo21c', 'u1');
|
||||||
|
await persistNodes(c, nodes, 'leo21c', 'u1'); // 再送一次
|
||||||
|
expect((await glossEntries(c)).length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('gloss 變更 → 更新既有 entry 內容(仍只一筆)', async () => {
|
||||||
|
const c = mockClient();
|
||||||
|
await persistNodes(c, [{ name: '長壽', id: 'longevity', gloss: '舊描述' }], 'leo21c');
|
||||||
|
await persistNodes(c, [{ name: '長壽', id: 'longevity', gloss: '活得更久本身具有價值' }], 'leo21c');
|
||||||
|
const entries = await glossEntries(c);
|
||||||
|
expect(entries.length).toBe(1);
|
||||||
|
expect(entries[0].content).toBe('長壽:活得更久本身具有價值');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('backfillGlossEntries — 存量:對既有 entity records 補 embeddable entry', () => {
|
||||||
|
// 直接以 entity record 造存量(模擬既有 16 筆),不經前向路徑。
|
||||||
|
async function seedEntity(
|
||||||
|
c: ReturnType<typeof mockClient>,
|
||||||
|
v: { canonical: string; node_id?: string; gloss?: string; embed?: string },
|
||||||
|
) {
|
||||||
|
await c.createRecord(TPL_ENTITY, {
|
||||||
|
canonical: v.canonical,
|
||||||
|
node_id: v.node_id ?? '',
|
||||||
|
aliases_json: '[]',
|
||||||
|
entity_type: '',
|
||||||
|
gloss: v.gloss ?? '',
|
||||||
|
embed: v.embed ?? 'true',
|
||||||
|
owner: '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
it('有 gloss 的既有 record → 落 entry;embed=false / 空 gloss → 略過', async () => {
|
||||||
|
const c = mockClient();
|
||||||
|
await seedEntity(c, { canonical: '長壽', node_id: 'longevity', gloss: '活得更久本身具有價值' });
|
||||||
|
await seedEntity(c, { canonical: '系統動力學', node_id: 'sd', gloss: '用因果圖表解複雜系統的方法論' });
|
||||||
|
await seedEntity(c, { canonical: '無標', node_id: 'x', gloss: '不該嵌', embed: 'false' });
|
||||||
|
await seedEntity(c, { canonical: '空描述', node_id: 'y' }); // 空 gloss
|
||||||
|
|
||||||
|
const res = await backfillGlossEntries(c);
|
||||||
|
expect(res.scanned).toBe(4);
|
||||||
|
expect(res.created).toBe(2);
|
||||||
|
expect(res.skipped).toBe(2); // embed=false + 空 gloss
|
||||||
|
|
||||||
|
const entries = await glossEntries(c);
|
||||||
|
expect(entries.length).toBe(2);
|
||||||
|
expect(entries.every((e) => JSON.parse(e.metadata_json!).embed === true)).toBe(true);
|
||||||
|
expect(entries.map((e) => e.content).sort()).toEqual(
|
||||||
|
['系統動力學:用因果圖表解複雜系統的方法論', '長壽:活得更久本身具有價值'].sort(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('冪等:連跑兩次 backfill → 不造重複(第二次全 unchanged)', async () => {
|
||||||
|
const c = mockClient();
|
||||||
|
await seedEntity(c, { canonical: '長壽', node_id: 'longevity', gloss: '活得更久本身具有價值' });
|
||||||
|
|
||||||
|
const first = await backfillGlossEntries(c);
|
||||||
|
expect(first.created).toBe(1);
|
||||||
|
|
||||||
|
const second = await backfillGlossEntries(c);
|
||||||
|
expect(second.created).toBe(0);
|
||||||
|
expect(second.unchanged).toBe(1);
|
||||||
|
expect((await glossEntries(c)).length).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
+16
-2
@@ -16,7 +16,16 @@ export class MockKbdbClient {
|
|||||||
|
|
||||||
async createEntry(input: any): Promise<BaseEntry> {
|
async createEntry(input: any): Promise<BaseEntry> {
|
||||||
const id = this.id('entry');
|
const id = this.id('entry');
|
||||||
const entry: BaseEntry = { id, content: input.content ?? null, entry_type: input.entry_type, owner_id: input.owner_id ?? null };
|
// 對齊 base entry-crud:保留 page_name(idempotency key)、metadata_json(embed 打標)、parent_id。
|
||||||
|
const entry: BaseEntry = {
|
||||||
|
id,
|
||||||
|
content: input.content ?? null,
|
||||||
|
entry_type: input.entry_type,
|
||||||
|
owner_id: input.owner_id ?? null,
|
||||||
|
parent_id: input.parent_id ?? null,
|
||||||
|
page_name: input.page_name ?? null,
|
||||||
|
metadata_json: input.metadata_json ?? null,
|
||||||
|
};
|
||||||
this.entries.set(id, entry);
|
this.entries.set(id, entry);
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
@@ -26,8 +35,13 @@ export class MockKbdbClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async listEntries(filters: any = {}): Promise<BaseEntry[]> {
|
async listEntries(filters: any = {}): Promise<BaseEntry[]> {
|
||||||
|
// 對齊 base entry-crud listEntries 的 exact-match filters(含 page_name idempotency key、parent_id)。
|
||||||
return [...this.entries.values()].filter(
|
return [...this.entries.values()].filter(
|
||||||
(e) => (!filters.entry_type || e.entry_type === filters.entry_type) && (!filters.owner_id || e.owner_id === filters.owner_id),
|
(e) =>
|
||||||
|
(!filters.entry_type || e.entry_type === filters.entry_type) &&
|
||||||
|
(!filters.owner_id || e.owner_id === filters.owner_id) &&
|
||||||
|
(!filters.parent_id || e.parent_id === filters.parent_id) &&
|
||||||
|
(!filters.page_name || e.page_name === filters.page_name),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user