您如何播种 mongodb 数据库,以便 Keystone 5 CMS 识别多对多关系?
How do you seed a mongodb database such that the Keystone 5 CMS recognizes the many-to-many relationships?
假设我有两个对象:产品和卖家
产品可以有多个卖家。
一个卖家可以销售多个产品。
目标是编写一个播种脚本,成功地为我的 MongoDB 数据库播种,以便 Keystone.js 的 CMS 识别多对多关系。
架构
Product.ts
import { text, relationship } from "@keystone-next/fields";
import { list } from "@keystone-next/keystone/schema";
export const Product = list({
fields: {
name: text({ isRequired: true }),
sellers: relationship({
ref: "Seller.products",
many: true,
}),
},
});
Seller.ts
import { text, relationship } from "@keystone-next/fields";
import { list } from "@keystone-next/keystone/schema";
export const Product = list({
fields: {
name: text({ isRequired: true }),
products: relationship({
ref: "Product.sellers",
many: true,
}),
},
});
KeystoneJS 配置
我的 keystone.ts
配置,为简洁起见缩短,如下所示:
import { insertSeedData } from "./seed-data"
...
db: {
adapter: "mongoose",
url: databaseURL,
async onConnect(keystone) {
console.log("Connected to the database!");
if (process.argv.includes("--seed-data")) {
await insertSeedData(keystone);
}
},
},
lists: createSchema({
Product,
Seller,
}),
...
播种脚本(这些是我希望更改的文件)
我有一个填充数据库的脚本 (seed-data/index.ts
):
import { products } from "./data";
import { sellers } from "./data";
export async function insertSeedData(ks: any) {
// setup code
const keystone = ks.keystone || ks;
const adapter = keystone.adapters?.MongooseAdapter || keystone.adapter;
const { mongoose } = adapter;
mongoose.set("debug", true);
// adding products to DB
for (const product of products) {
await mongoose.model("Product").create(product);
}
// adding sellers to DB
for (const seller of sellers) {
await mongoose.model("Seller").create(seller);
}
}
最后,data.ts
看起来像这样:
export const products = [
{
name: "apple",
sellers: ["Joe", "Anne", "Duke", "Alicia"],
},
{
name: "orange",
sellers: ["Duke", "Alicia"],
},
...
];
export const sellers = [
{
name: "Joe",
products: ["apple", "banana"],
},
{
name: "Duke",
products: ["apple", "orange", "banana"],
},
...
];
由于多种原因,上述设置不起作用。最明显的是 Product
和 Seller
对象(分别)的 sellers
和 products
属性应该引用对象(ObjectId
)而不是名称(例如“苹果”,“乔”)。
我会 post 在下面进行几次我认为可行但没有成功的尝试:
尝试 1
我想我只是给他们临时 ID(下面 data.ts
中的 id
属性)然后,一旦 MongoDB 分配一个 ObjectId
,我'我会用的。
seed-data/index.ts
...
const productIdsMapping = [];
...
// adding products to DB
for (const product of products) {
const productToPutInMongoDB = { name: product.name };
const { _id } = await mongoose.model("Product").create(productToPutInMongoDB);
productIdsMapping.push(_id);
}
// adding sellers to DB (using product IDs created by MongoDB)
for (const seller of sellers) {
const productMongoDBIds = [];
for (const productSeedId of seller.products) {
productMongoDBIds.push(productIdsMapping[productSeedId]);
const sellerToPutInMongoDB = { name: seller.name, products: productMongoDBIds };
await mongoose.model("Seller").create(sellerToPutInMongoDB);
}
...
data.ts
export const products = [
{
id: 0,
name: "apple",
sellers: [0, 1, 2, 3],
},
{
id: 1,
name: "orange",
sellers: [2, 3],
},
...
];
export const sellers = [
{
id: 0
name: "Joe",
products: [0, 2],
},
...
{
id: 2
name: "Duke",
products: [0, 1, 2],
},
...
];
输出(尝试 1):
它似乎并不关心或承认 products
属性。
Mongoose: sellers.insertOne({ _id: ObjectId("$ID"), name: 'Joe', __v: 0}, { session: null })
{
results: {
_id: $ID,
name: 'Joe',
__v: 0
}
}
尝试 2
我想也许出于某种原因我没有正确格式化它,所以也许如果我查询产品并将它们直接推到卖家对象中,那会起作用。
seed-data/index.ts
...
const productIdsMapping = [];
...
// adding products to DB
for (const product of products) {
const productToPutInMongoDB = { name: product.name };
const { _id } = await mongoose.model("Product").create(productToPutInMongoDB);
productIdsMapping.push(_id);
}
// adding sellers to DB (using product IDs created by MongoDB)
for (const seller of sellers) {
const productMongoDBIds = [];
for (const productSeedId of seller.products) {
productMongoDBIds.push(productIdsMapping[productSeedId]);
}
const sellerToPutInMongoDB = { name: seller.name };
const { _id } = await mongoose.model("Seller").create(sellerToPutInMongoDB);
const resultsToBeConsoleLogged = await mongoose.model("Seller").findByIdAndUpdate(
_id,
{
$push: {
products: productMongoDBIds,
},
},
{ new: true, useFindAndModify: false, upsert: true }
);
}
...
data.ts
与尝试 1 相同的 data.ts 文件。
输出(尝试 2):
同样的事情。 products
属性没有出现。
Mongoose: sellers.insertOne({ _id: ObjectId("$ID"), name: 'Joe', __v: 0}, { session: null })
{
results: {
_id: $ID,
name: 'Joe',
__v: 0
}
}
所以,现在我卡住了。我认为尝试 1 会像这样的答案 Just Work™:
有什么想法吗?
我找到了解决办法。这是背景:
当我定义架构时,Keystone 会创建相应的 MongoDB 集合。如果对象A和对象B之间存在多对多关系,Keystone会创建3个集合:A、B和A_relationshipToB_B_relationshipToA.
第三个集合是两者之间的接口。它只是一个包含来自 A 和 B 的 ID 对的集合。
因此,为了用 Keystone CMS 中显示的多对多关系为我的数据库做种,我不仅要为 A 和 B 做种,还要为第 3 个集合做种:A_relationshipToB_B_relationshipToA .
因此,seed-data/index.ts
将有一些代码插入 table:
...
for (const seller of sellers) {
const sellerToAdd = { name: seller.name };
const { _id } = await mongoose.model("Seller").create(sellerToAdd);
// Product_sellers_Seller_products Insertion
for (const productId of seller.products) {
await mongoose
.model("Product_sellers_Seller_products")
.create({
Product_left_id: productIds[productId], // (data.ts id) --> (Mongo ID)
Seller_right_id: _id,
});
}
}
...
假设我有两个对象:产品和卖家
产品可以有多个卖家。 一个卖家可以销售多个产品。
目标是编写一个播种脚本,成功地为我的 MongoDB 数据库播种,以便 Keystone.js 的 CMS 识别多对多关系。
架构
Product.ts
import { text, relationship } from "@keystone-next/fields";
import { list } from "@keystone-next/keystone/schema";
export const Product = list({
fields: {
name: text({ isRequired: true }),
sellers: relationship({
ref: "Seller.products",
many: true,
}),
},
});
Seller.ts
import { text, relationship } from "@keystone-next/fields";
import { list } from "@keystone-next/keystone/schema";
export const Product = list({
fields: {
name: text({ isRequired: true }),
products: relationship({
ref: "Product.sellers",
many: true,
}),
},
});
KeystoneJS 配置
我的 keystone.ts
配置,为简洁起见缩短,如下所示:
import { insertSeedData } from "./seed-data"
...
db: {
adapter: "mongoose",
url: databaseURL,
async onConnect(keystone) {
console.log("Connected to the database!");
if (process.argv.includes("--seed-data")) {
await insertSeedData(keystone);
}
},
},
lists: createSchema({
Product,
Seller,
}),
...
播种脚本(这些是我希望更改的文件)
我有一个填充数据库的脚本 (seed-data/index.ts
):
import { products } from "./data";
import { sellers } from "./data";
export async function insertSeedData(ks: any) {
// setup code
const keystone = ks.keystone || ks;
const adapter = keystone.adapters?.MongooseAdapter || keystone.adapter;
const { mongoose } = adapter;
mongoose.set("debug", true);
// adding products to DB
for (const product of products) {
await mongoose.model("Product").create(product);
}
// adding sellers to DB
for (const seller of sellers) {
await mongoose.model("Seller").create(seller);
}
}
最后,data.ts
看起来像这样:
export const products = [
{
name: "apple",
sellers: ["Joe", "Anne", "Duke", "Alicia"],
},
{
name: "orange",
sellers: ["Duke", "Alicia"],
},
...
];
export const sellers = [
{
name: "Joe",
products: ["apple", "banana"],
},
{
name: "Duke",
products: ["apple", "orange", "banana"],
},
...
];
由于多种原因,上述设置不起作用。最明显的是 Product
和 Seller
对象(分别)的 sellers
和 products
属性应该引用对象(ObjectId
)而不是名称(例如“苹果”,“乔”)。
我会 post 在下面进行几次我认为可行但没有成功的尝试:
尝试 1
我想我只是给他们临时 ID(下面 data.ts
中的 id
属性)然后,一旦 MongoDB 分配一个 ObjectId
,我'我会用的。
seed-data/index.ts
...
const productIdsMapping = [];
...
// adding products to DB
for (const product of products) {
const productToPutInMongoDB = { name: product.name };
const { _id } = await mongoose.model("Product").create(productToPutInMongoDB);
productIdsMapping.push(_id);
}
// adding sellers to DB (using product IDs created by MongoDB)
for (const seller of sellers) {
const productMongoDBIds = [];
for (const productSeedId of seller.products) {
productMongoDBIds.push(productIdsMapping[productSeedId]);
const sellerToPutInMongoDB = { name: seller.name, products: productMongoDBIds };
await mongoose.model("Seller").create(sellerToPutInMongoDB);
}
...
data.ts
export const products = [
{
id: 0,
name: "apple",
sellers: [0, 1, 2, 3],
},
{
id: 1,
name: "orange",
sellers: [2, 3],
},
...
];
export const sellers = [
{
id: 0
name: "Joe",
products: [0, 2],
},
...
{
id: 2
name: "Duke",
products: [0, 1, 2],
},
...
];
输出(尝试 1):
它似乎并不关心或承认 products
属性。
Mongoose: sellers.insertOne({ _id: ObjectId("$ID"), name: 'Joe', __v: 0}, { session: null })
{
results: {
_id: $ID,
name: 'Joe',
__v: 0
}
}
尝试 2
我想也许出于某种原因我没有正确格式化它,所以也许如果我查询产品并将它们直接推到卖家对象中,那会起作用。
seed-data/index.ts
...
const productIdsMapping = [];
...
// adding products to DB
for (const product of products) {
const productToPutInMongoDB = { name: product.name };
const { _id } = await mongoose.model("Product").create(productToPutInMongoDB);
productIdsMapping.push(_id);
}
// adding sellers to DB (using product IDs created by MongoDB)
for (const seller of sellers) {
const productMongoDBIds = [];
for (const productSeedId of seller.products) {
productMongoDBIds.push(productIdsMapping[productSeedId]);
}
const sellerToPutInMongoDB = { name: seller.name };
const { _id } = await mongoose.model("Seller").create(sellerToPutInMongoDB);
const resultsToBeConsoleLogged = await mongoose.model("Seller").findByIdAndUpdate(
_id,
{
$push: {
products: productMongoDBIds,
},
},
{ new: true, useFindAndModify: false, upsert: true }
);
}
...
data.ts
与尝试 1 相同的 data.ts 文件。
输出(尝试 2):
同样的事情。 products
属性没有出现。
Mongoose: sellers.insertOne({ _id: ObjectId("$ID"), name: 'Joe', __v: 0}, { session: null })
{
results: {
_id: $ID,
name: 'Joe',
__v: 0
}
}
所以,现在我卡住了。我认为尝试 1 会像这样的答案 Just Work™:
有什么想法吗?
我找到了解决办法。这是背景:
当我定义架构时,Keystone 会创建相应的 MongoDB 集合。如果对象A和对象B之间存在多对多关系,Keystone会创建3个集合:A、B和A_relationshipToB_B_relationshipToA.
第三个集合是两者之间的接口。它只是一个包含来自 A 和 B 的 ID 对的集合。
因此,为了用 Keystone CMS 中显示的多对多关系为我的数据库做种,我不仅要为 A 和 B 做种,还要为第 3 个集合做种:A_relationshipToB_B_relationshipToA .
因此,seed-data/index.ts
将有一些代码插入 table:
...
for (const seller of sellers) {
const sellerToAdd = { name: seller.name };
const { _id } = await mongoose.model("Seller").create(sellerToAdd);
// Product_sellers_Seller_products Insertion
for (const productId of seller.products) {
await mongoose
.model("Product_sellers_Seller_products")
.create({
Product_left_id: productIds[productId], // (data.ts id) --> (Mongo ID)
Seller_right_id: _id,
});
}
}
...