仅当使用 knex 唯一时才插入

Posted

技术标签:

【中文标题】仅当使用 knex 唯一时才插入【英文标题】:Insert only if unique using knex 【发布时间】:2019-07-12 16:43:42 【问题描述】:

我正在编写一个 Node.js 脚本,它接受一组对象,这些对象代表记录不同测量值的不同设备。我想使用 Knex.js 将有关设备的信息存储在 PSQL 数据库中,但只想存储来自表示新/唯一设备的对象的设备信息。只要同一设备出现在不同的 POST 请求中,在插入之前验证 device_id 似乎就可以工作。但是当同一个设备出现时 在同一个 POST 请求中,似乎程序的异步特性导致验证在插入完成之前发生。

我试图让脚本调用两个单独的异步等待函数(一个用于验证,另一个用于实际插入),但不确定这是否是最简单的方法,或者我是否做对了,因为它无论如何都失败了。

app.post('/testsite', (req, res) => 
  const data = (req.body.measurements);
  for (let i = 0; i < data.length; i++) 
    database.select("device_id").from("device_info").where("device_id", data[i].device_id)
      .then(MatchList => 
        console.log(MatchList);
        if (MatchList.length === 0) 
          return database('device_info')
            .returning('device_id')
            .insert(
              device_id: data[i].device_id,
              device_name: data[i].device_name,
              site_id: data[i].site_id,
              site_name: data[i].site_name
            )
            .then((newdevice) => 
              console.log('inserted device id', newdevice);
            );
        
        return;
      );
  
);

我希望它在验证失败时不会插入,但似乎验证永远不会失败,即使它应该失败,我得到这个错误:

Unhandled rejection error: duplicate key value violates unique constraint "device_info_pkey"

【问题讨论】:

哪个验证? @alfasin 验证是当我选择数据库中 device_id === data[i].device_id 的所有条目并确保所有这些条目的长度为 0 时。基本上没有这样设备退出。当且仅当这是真的,那么应该尝试插入。 你在使用knex吗? @technogeek1995 是的! 【参考方案1】:

我不确定问题是由于 knex 的异步特性,还是其他原因。但是,我重新编写了您的代码以使用 async/await 语法以使其更具可读性。由于我添加了.first(),我还只是检查设备是否返回为空而不是数组。您能否检查一下device 在您调用此函数时是否记录了控制台?

app.post('/testsite', async (req, res) => 
  const data = (req.body.measurements);
  for (let i = 0; i < data.length; i++) 
    const device = await database
      .select('*')
      .from('device_info')
      .where('device_id', data[i].device_id)
      .first();
    console.log(device);
    if (device) 
      const newDevice = await database
        .from('device_info')
        .insert(
          device_id: data[i].device_id,
          device_name: data[i].device_name,
          site_id: data[i].site_id,
          site_name: data[i].site_name
        )
        .returning('*');

      return newDevice;
     else 
      return device;
    
  
);

【讨论】:

所以我不得不将 for 循环的内容移动到一个名为 postfxn 的函数中,然后将该函数定义为 POST 请求之外的异步函数,以便使用 await 关键字。但是,这仍然没有解决问题。控制台日志以未定义的形式返回,如果我删除 .first() 它以空数组的形式返回 即使您期望有一个值,它也会以未定义的形式返回?你确定data[i].device_id 不是未定义的吗? 它被明确定义为我使用 data[i].device_id 进行实际插入并且有效。即使 PSQL 数据库显示插入已完成,删除 .first() 也只会返回一个空数组,这一事实使我相信,由于 javascript 是异步的,因此 for 循环在可以插入前一个设备之前转到下一个查询跨度> 上面写的方式(使用 async/await),异步应该没有任何问题。 await 是一个阻塞调用,它使代码看起来像是同步代码。你能编辑你的问题并从psql解释一个\d device_info吗?【参考方案2】:

我终于能够解决这个问题。本质上,仅在搜索和插入上使用 async await 是不够的,因为 for 循环仍会移动到下一个元素。为了解决这个问题,我让 POST 请求调用了一个异步函数。

app.post('/testsite', (req,res) => 

 const data = (req.body.measurements);

    postfxn(data);  )

此函数等待 for 循环的每次迭代。

async function postfxn(data)

for(let i =0; i<data.length; i++)
     await insertdevice(data[i]); 
  

插入设备功能然后使用异步等待来搜索设备,如果它不在数据库中,则插入设备,如 technogeek1996 的回答所建议的那样。

希望这可以帮助其他有类似问题的人

【讨论】:

这仍然行不通,因为app.post('/testsite', (req,res) =&gt; 不使用async,它必须使用异步函数。另外,你在调用postfxn时没有使用await,这意味着它不会做任何事情。

以上是关于仅当使用 knex 唯一时才插入的主要内容,如果未能解决你的问题,请参考以下文章

PostgreSQL:仅当元素唯一时才将元素附加到 jsonb 数组

MSSQL - 仅当所有值都不为空时才插入值

仅当表中不存在两个 id 的组合时才将值插入表中

MongoDB/Mongoose - 仅当某个字段是唯一的时才将对象添加到对象数组中

仅当行不存在时才插入 SQL

仅当记录不存在时才将 SQL 插入表中[重复]