Sequelize 中文文檔 v4 - Model usage - 模型使用

Model usage - 模型使用

此係列文章的應用示例已發佈於 GitHub: sequelize-docs-Zh-CN. 能夠 Fork 幫助改進或 Star 關注更新. 歡迎 Star.html

數據檢索/查找器

Finder 方法旨在從數據庫查詢數據。 他們 返回簡單的對象,而是返回模型實例。 由於 finder 方法返回模型實例,您能夠按照 實例 的文檔中所述,爲結果調用任何模型實例成員。git

在本文中,咱們將探討 finder 方法能夠作什麼:github

find - 搜索數據庫中的一個特定元素

// 搜索已知的ids
Project.findById(123).then(project => {
  // project 將是 Project的一個實例,並具備在表中存爲 id 123 條目的內容。
  // 若是沒有定義這樣的條目,你將得到null
})

// 搜索屬性
Project.findOne({ where: {title: 'aProject'} }).then(project => {
  // project 將是 Projects 表中 title 爲 'aProject'  的第一個條目 || null
})


Project.findOne({
  where: {title: 'aProject'},
  attributes: ['id', ['name', 'title']]
}).then(project => {
  // project 將是 Projects 表中 title 爲 'aProject'  的第一個條目 || null
  // project.title 將包含 project 的 name
})

findOrCreate - 搜索特定元素或建立它(若是不可用)

方法 findOrCreate 可用於檢查數據庫中是否已存在某個元素。 若是是這種狀況,則該方法將生成相應的實例。 若是元素不存在,將會被建立。sql

若是是這種狀況,則該方法將致使相應的實例。 若是元素不存在,將會被建立。數據庫

假設咱們有一個空的數據庫,一個 User 模型有一個 usernamejob數組

User
  .findOrCreate({where: {username: 'sdepold'}, defaults: {job: 'Technical Lead JavaScript'}})
  .spread((user, created) => {
    console.log(user.get({
      plain: true
    }))
    console.log(created)

    /*
    findOrCreate 返回一個包含已找到或建立的對象的數組,找到或建立的對象和一個布爾值,若是建立一個新對象將爲true,不然爲false,像這樣:

    [ {
        username: 'sdepold',
        job: 'Technical Lead JavaScript',
        id: 1,
        createdAt: Fri Mar 22 2013 21: 28: 34 GMT + 0100(CET),
        updatedAt: Fri Mar 22 2013 21: 28: 34 GMT + 0100(CET)
      },
      true ]

在上面的例子中,".spread" 將數組分紅2部分,並將它們做爲參數傳遞給回調函數,在這種狀況下將它們視爲 "user" 和 "created" 。(因此「user」將是返回數組的索引0的對象,而且 "created" 將等於 "true"。)
    */
  })

代碼建立了一個新的實例。 因此當咱們已經有一個實例了 ...函數

User.create({ username: 'fnord', job: 'omnomnom' })
  .then(() => User.findOrCreate({where: {username: 'fnord'}, defaults: {job: 'something else'}}))
  .spread((user, created) => {
    console.log(user.get({
      plain: true
    }))
    console.log(created)

    /*
    在這個例子中,findOrCreate 返回一個以下的數組:
    [ {
        username: 'fnord',
        job: 'omnomnom',
        id: 2,
        createdAt: Fri Mar 22 2013 21: 28: 34 GMT + 0100(CET),
        updatedAt: Fri Mar 22 2013 21: 28: 34 GMT + 0100(CET)
      },
      false
    ]
    由findOrCreate返回的數組經過 ".spread" 擴展爲兩部分,而且這些部分將做爲2個參數傳遞給回調函數,在這種狀況下將其視爲 "user" 和 "created" 。(因此「user」將是返回數組的索引0的對象,而且 "created" 將等於 "false"。)
    */
  })

...現有條目將不會更改。 看到第二個用戶的 "job",而且實際上建立操做是假的。ui

findAndCountAll - 在數據庫中搜索多個元素,返回數據和總計數

這是一個方便的方法,它結合了 findAllcount(見下文),當處理與分頁相關的查詢時,這是有用的,你想用 limitoffset 檢索數據,但也須要知道總數與查詢匹配的記錄數:code

處理程序成功將始終接收具備兩個屬性的對象:htm

  • count - 一個整數,總數記錄匹配where語句
  • rows - 一個數組對象,記錄在limit和offset範圍內匹配where語句,
Project
  .findAndCountAll({
     where: {
        title: {
          [Op.like]: 'foo%'
        }
     },
     offset: 10,
     limit: 2
  })
  .then(result => {
    console.log(result.count);
    console.log(result.rows);
  });

findAndCountAll 也支持 include。 只有標記爲 required 的 include 將被添加到計數部分:

假設您想查找附有我的資料的全部用戶:

User.findAndCountAll({
  include: [
     { model: Profile, required: true}
  ],
  limit: 3
});

由於 Profile 的 include 有 required 設置,這將致使內部鏈接,而且只有具備 profile 的用戶將被計數。 若是咱們從 include 中刪除required,那麼有和沒有 profile 的用戶都將被計數。 在include中添加一個 where 語句會自動使它成爲 required:

User.findAndCountAll({
  include: [
     { model: Profile, where: { active: true }}
  ],
  limit: 3
});

上面的查詢只會對具備 active profile 的用戶進行計數,由於在將 where 語句添加到 include 時,required 被隱式設置爲 true。

傳遞給 findAndCountAll 的 options 對象與 findAll 相同(以下所述)。

findAll - 搜索數據庫中的多個元素

// 找到多個條目
Project.findAll().then(projects => {
  // projects 將是全部 Project 實例的數組
})

// 也能夠:
Project.all().then(projects => {
  // projects 將是全部 Project 實例的數組
})

// 搜索特定屬性 - 使用哈希
Project.findAll({ where: { name: 'A Project' } }).then(projects => {
  // projects將是一個具備指定 name 的 Project 實例數組
})

// 在特定範圍內進行搜索
Project.findAll({ where: { id: [1,2,3] } }).then(projects => {
  // projects將是一系列具備 id 1,2 或 3 的項目
  // 這其實是在作一個 IN 查詢
})

Project.findAll({
  where: {
    id: {
      [Op.and]: {a: 5},           // 且 (a = 5)
      [Op.or]: [{a: 5}, {a: 6}],  // (a = 5 或 a = 6)
      [Op.gt]: 6,                // id > 6
      [Op.gte]: 6,               // id >= 6
      [Op.lt]: 10,               // id < 10
      [Op.lte]: 10,              // id <= 10
      [Op.ne]: 20,               // id != 20
      [Op.between]: [6, 10],     // 在 6 和 10 之間
      [Op.notBetween]: [11, 15], // 不在 11 和 15 之間
      [Op.in]: [1, 2],           // 在 [1, 2] 之中
      [Op.notIn]: [1, 2],        // 不在 [1, 2] 之中
      [Op.like]: '%hat',         // 包含 '%hat'
      [Op.notLike]: '%hat',       // 不包含 '%hat'
      [Op.iLike]: '%hat',         // 包含 '%hat' (不區分大小寫)  (僅限 PG)
      [Op.notILike]: '%hat',      // 不包含 '%hat'  (僅限 PG)
      [Op.overlap]: [1, 2],       // && [1, 2] (PG數組重疊運算符)
      [Op.contains]: [1, 2],      // @> [1, 2] (PG數組包含運算符)
      [Op.contained]: [1, 2],     // <@ [1, 2] (PG數組包含於運算符)
      [Op.any]: [2,3],            // 任何數組[2, 3]::INTEGER (僅限 PG)
    },
    status: {
      [Op.not]: false,           // status 不爲 FALSE
    }
  }
})

複合過濾 / OR / NOT 查詢

你可使用多層嵌套的 AND,OR 和 NOT 條件進行一個複合的 where 查詢。 爲了作到這一點,你可使用 orandnot 運算符:

Project.findOne({
  where: {
    name: 'a project',
    [Op.or]: [
      { id: [1,2,3] },
      { id: { [Op.gt]: 10 } }
    ]
  }
})

Project.findOne({
  where: {
    name: 'a project',
    id: {
      [Op.or]: [
        [1,2,3],
        { [Op.gt]: 10 }
      ]
    }
  }
})

這兩段代碼將生成如下內容:

SELECT *
FROM `Projects`
WHERE (
  `Projects`.`name` = 'a project'
   AND (`Projects`.`id` IN (1,2,3) OR `Projects`.`id` > 10)
)
LIMIT 1;

not 示例:

Project.findOne({
  where: {
    name: 'a project',
    [Op.not]: [
      { id: [1,2,3] },
      { array: { [Op.contains]: [3,4,5] } }
    ]
  }
});

將生成:

SELECT *
FROM `Projects`
WHERE (
  `Projects`.`name` = 'a project'
   AND NOT (`Projects`.`id` IN (1,2,3) OR `Projects`.`array` @> ARRAY[3,4,5]::INTEGER[])
)
LIMIT 1;

用限制,偏移,順序和分組操做數據集

要獲取更多相關數據,可使用限制,偏移,順序和分組:

// 限制查詢的結果
Project.findAll({ limit: 10 })

// 跳過前10個元素
Project.findAll({ offset: 10 })

// 跳過前10個元素,並獲取2個
Project.findAll({ offset: 10, limit: 2 })

分組和排序的語法是相同的,因此下面只用一個單獨的例子來解釋分組,而其他的則是排序。 您下面看到的全部內容也能夠對分組進行

Project.findAll({order: 'title DESC'})
// 生成 ORDER BY title DESC

Project.findAll({group: 'name'})
// 生成 GROUP BY name

請注意,在上述兩個示例中,提供的字符串逐字插入到查詢中,因此不會轉義列名稱。 當你向 order / group 提供字符串時,將始終如此。 若是要轉義列名,您應該提供一個參數數組,即便您只想經過單個列進行 order / group

something.findOne({
  order: [
    'name',
    // 將返回 `name`
    'username DESC',
    // 將返回 `username DESC` -- 不要這樣作
    ['username', 'DESC'],
    // 將返回 `username` DESC
    sequelize.fn('max', sequelize.col('age')),
    // 將返回 max(`age`)
    [sequelize.fn('max', sequelize.col('age')), 'DESC'],
    // 將返回 max(`age`) DESC
    [sequelize.fn('otherfunction', sequelize.col('col1'), 12, 'lalala'), 'DESC'],
    // 將返回 otherfunction(`col1`, 12, 'lalala') DESC
    [sequelize.fn('otherfunction', sequelize.fn('awesomefunction', sequelize.col('col'))), 'DESC']
    // 將返回 otherfunction(awesomefunction(`col`)) DESC,這個嵌套是能夠無限的!
  ]
})

回顧一下,order / group數組的元素能夠是如下內容:

  • String - 將被引用
  • Array - 第一個元素將被引用,第二個將被逐字地追加
  • Object -

    • raw 將被添加逐字引用
    • 若是未設置 raw,一切都被忽略,查詢將失敗
  • Sequelize.fn 和 Sequelize.col 返回函數和引用的列

原始查詢

有時候,你可能會期待一個你想要顯示的大量數據集,而無需操做。 對於你選擇的每一行,Sequelize 建立一個具備更新,刪除和獲取關聯等功能的實例。若是您有數千行,則可能須要一些時間。 若是您只須要原始數據,而且不想更新任何內容,您能夠這樣作來獲取原始數據。

// 你指望從數據庫的一個巨大的數據集,
// 而且不想花時間爲每一個條目構建DAO?
// 您能夠傳遞一個額外的查詢參數來取代原始數據:
Project.findAll({ where: { ... }, raw: true })

count - 計算數據庫中元素的出現次數

還有一種數據庫對象計數的方法:

Project.count().then(c => {
  console.log("There are " + c + " projects!")
})

Project.count({ where: {'id': {[Op.gt]: 25}} }).then(c => {
  console.log("There are " + c + " projects with an id greater than 25.")
})

max - 獲取特定表中特定屬性的最大值

這裏是獲取屬性的最大值的方法:

/*
   咱們假設3個具備屬性年齡的對象。
   第一個是10歲,
   第二個是5歲,
   第三個是40歲。
*/
Project.max('age').then(max => {
  // 將返回 40
})

Project.max('age', { where: { age: { [Op.lt]: 20 } } }).then(max => {
  // 將會是 10
})

min - 獲取特定表中特定屬性的最小值

這裏是獲取屬性的最小值的方法:

/*
   咱們假設3個具備屬性年齡的對象。
   第一個是10歲,
   第二個是5歲,
   第三個是40歲。
*/
Project.min('age').then(min => {
  // 將返回 5
})

Project.min('age', { where: { age: { [Op.gt]: 5 } } }).then(min => {
  // 將會是 10
})

sum - 特定屬性的值求和

爲了計算表的特定列的總和,可使用「sum」方法。

/*
   咱們假設3個具備屬性年齡的對象。
   第一個是10歲,
   第二個是5歲,
   第三個是40歲。
*/
Project.sum('age').then(sum => {
  // 將返回 55
})

Project.sum('age', { where: { age: { [Op.gt]: 5 } } }).then(sum => {
  // 將會是 50
})

預加載

當你從數據庫檢索數據時,也想同時得到與之相關聯的查詢,這被稱爲預加載。這個基本思路就是當你調用 findfindAll 時使用 include 屬性。讓咱們假設如下設置:

const User = sequelize.define('user', { name: Sequelize.STRING })
const Task = sequelize.define('task', { name: Sequelize.STRING })
const Tool = sequelize.define('tool', { name: Sequelize.STRING })

Task.belongsTo(User)
User.hasMany(Task)
User.hasMany(Tool, { as: 'Instruments' })

sequelize.sync().then(() => {
  // 這是咱們繼續的地方 ...
})

首先,讓咱們用它們的關聯 user 加載全部的 task。

Task.findAll({ include: [ User ] }).then(tasks => {
  console.log(JSON.stringify(tasks))

  /*
    [{
      "name": "A Task",
      "id": 1,
      "createdAt": "2013-03-20T20:31:40.000Z",
      "updatedAt": "2013-03-20T20:31:40.000Z",
      "userId": 1,
      "user": {
        "name": "John Doe",
        "id": 1,
        "createdAt": "2013-03-20T20:31:45.000Z",
        "updatedAt": "2013-03-20T20:31:45.000Z"
      }
    }]
  */
})

請注意,訪問者(結果實例中的 User 屬性)是單數形式,由於關聯是一對一的。

接下來的事情:用多對一的關聯加載數據!

User.findAll({ include: [ Task ] }).then(users => {
  console.log(JSON.stringify(users))

  /*
    [{
      "name": "John Doe",
      "id": 1,
      "createdAt": "2013-03-20T20:31:45.000Z",
      "updatedAt": "2013-03-20T20:31:45.000Z",
      "tasks": [{
        "name": "A Task",
        "id": 1,
        "createdAt": "2013-03-20T20:31:40.000Z",
        "updatedAt": "2013-03-20T20:31:40.000Z",
        "userId": 1
      }]
    }]
  */
})

請注意,訪問者(結果實例中的 Tasks 屬性)是複數形式,由於關聯是多對一的。

若是關聯是別名的(使用 as 參數),則在包含模型時必須指定此別名。 注意用戶的 Tool 如何被別名爲 Instruments。 爲了得到正確的權限,您必須指定要加載的模型以及別名:

User.findAll({ include: [{ model: Tool, as: 'Instruments' }] }).then(users => {
  console.log(JSON.stringify(users))

  /*
    [{
      "name": "John Doe",
      "id": 1,
      "createdAt": "2013-03-20T20:31:45.000Z",
      "updatedAt": "2013-03-20T20:31:45.000Z",
      "Instruments": [{
        "name": "Toothpick",
        "id": 1,
        "createdAt": null,
        "updatedAt": null,
        "userId": 1
      }]
    }]
  */
})

您還能夠經過指定與關聯別名匹配的字符串來包含別名:

User.findAll({ include: ['Instruments'] }).then(users => {
  console.log(JSON.stringify(users))

  /*
    [{
      "name": "John Doe",
      "id": 1,
      "createdAt": "2013-03-20T20:31:45.000Z",
      "updatedAt": "2013-03-20T20:31:45.000Z",
      "Instruments": [{
        "name": "Toothpick",
        "id": 1,
        "createdAt": null,
        "updatedAt": null,
        "userId": 1
      }]
    }]
  */
})

User.findAll({ include: [{ association: 'Instruments' }] }).then(users => {
  console.log(JSON.stringify(users))

  /*
    [{
      "name": "John Doe",
      "id": 1,
      "createdAt": "2013-03-20T20:31:45.000Z",
      "updatedAt": "2013-03-20T20:31:45.000Z",
      "Instruments": [{
        "name": "Toothpick",
        "id": 1,
        "createdAt": null,
        "updatedAt": null,
        "userId": 1
      }]
    }]
  */
})

當預加載時,咱們也可使用 where 過濾關聯的模型。 這將返回 Tool 模型中全部與 where 語句匹配的行的User

User.findAll({
    include: [{
        model: Tool,
        as: 'Instruments',
        where: { name: { [Op.like]: '%ooth%' } }
    }]
}).then(users => {
    console.log(JSON.stringify(users))

    /*
      [{
        "name": "John Doe",
        "id": 1,
        "createdAt": "2013-03-20T20:31:45.000Z",
        "updatedAt": "2013-03-20T20:31:45.000Z",
        "Instruments": [{
          "name": "Toothpick",
          "id": 1,
          "createdAt": null,
          "updatedAt": null,
          "userId": 1
        }]
      }],

      [{
        "name": "John Smith",
        "id": 2,
        "createdAt": "2013-03-20T20:31:45.000Z",
        "updatedAt": "2013-03-20T20:31:45.000Z",
        "Instruments": [{
          "name": "Toothpick",
          "id": 1,
          "createdAt": null,
          "updatedAt": null,
          "userId": 1
        }]
      }],
    */
  })

當使用 include.where 過濾一個預加載的模型時,include.required 被隱式設置爲 true。 這意味着內部聯接完成返回具備任何匹配子項的父模型。

使用預加載模型的頂層 WHERE

將模型的 WHERE 條件從 ON 條件的 include 模式移動到頂層,你可使用 '$nested.column$' 語法:

User.findAll({
    where: {
        '$Instruments.name$': { [Op.iLike]: '%ooth%' }
    },
    include: [{
        model: Tool,
        as: 'Instruments'
    }]
}).then(users => {
    console.log(JSON.stringify(users));

    /*
      [{
        "name": "John Doe",
        "id": 1,
        "createdAt": "2013-03-20T20:31:45.000Z",
        "updatedAt": "2013-03-20T20:31:45.000Z",
        "Instruments": [{
          "name": "Toothpick",
          "id": 1,
          "createdAt": null,
          "updatedAt": null,
          "userId": 1
        }]
      }],

      [{
        "name": "John Smith",
        "id": 2,
        "createdAt": "2013-03-20T20:31:45.000Z",
        "updatedAt": "2013-03-20T20:31:45.000Z",
        "Instruments": [{
          "name": "Toothpick",
          "id": 1,
          "createdAt": null,
          "updatedAt": null,
          "userId": 1
        }]
      }],
    */

包括全部

要包含全部屬性,您可使用 all:true 傳遞單個對象:

User.findAll({ include: [{ all: true }]});

包括軟刪除的記錄

若是想要加載軟刪除的記錄,能夠經過將 include.paranoid 設置爲 false 來實現

User.findAll({
    include: [{
        model: Tool,
        where: { name: { [Op.like]: '%ooth%' } },
        paranoid: false // query and loads the soft deleted records
    }]
});

排序預加載關聯

在一對多關係的狀況下。

Company.findAll({ include: [ Division ], order: [ [ Division, 'name' ] ] });
Company.findAll({ include: [ Division ], order: [ [ Division, 'name', 'DESC' ] ] });
Company.findAll({
  include: [ { model: Division, as: 'Div' } ],
  order: [ [ { model: Division, as: 'Div' }, 'name' ] ]
});
Company.findAll({
  include: [ { model: Division, as: 'Div' } ],
  order: [ [ { model: Division, as: 'Div' }, 'name', 'DESC' ] ]
});
Company.findAll({
  include: [ { model: Division, include: [ Department ] } ],
  order: [ [ Division, Department, 'name' ] ]
});

在多對多關係的狀況下,您還能夠經過表中的屬性進行排序。

Company.findAll({
  include: [ { model: Division, include: [ Department ] } ],
  order: [ [ Division, DepartmentDivision, 'name' ] ]
});

嵌套預加載

您可使用嵌套的預加載來加載相關模型的全部相關模型:

User.findAll({
  include: [
    {model: Tool, as: 'Instruments', include: [
      {model: Teacher, include: [ /* etc */]}
    ]}
  ]
}).then(users => {
  console.log(JSON.stringify(users))

  /*
    [{
      "name": "John Doe",
      "id": 1,
      "createdAt": "2013-03-20T20:31:45.000Z",
      "updatedAt": "2013-03-20T20:31:45.000Z",
      "Instruments": [{ // 1:M and N:M association
        "name": "Toothpick",
        "id": 1,
        "createdAt": null,
        "updatedAt": null,
        "userId": 1,
        "Teacher": { // 1:1 association
          "name": "Jimi Hendrix"
        }
      }]
    }]
  */
})

這將產生一個外鏈接。 可是,相關模型上的 where 語句將建立一個內部鏈接,並僅返回具備匹配子模型的實例。 要返回全部父實例,您應該添加 required: false

User.findAll({
  include: [{
    model: Tool,
    as: 'Instruments',
    include: [{
      model: Teacher,
      where: {
        school: "Woodstock Music School"
      },
      required: false
    }]
  }]
}).then(users => {
  /* ... */
})

以上查詢將返回全部用戶及其全部樂器,但只會返回與 Woodstock Music School 相關的老師。

包括全部也支持嵌套加載:

User.findAll({ include: [{ all: true, nested: true }]});

若是這篇文章對您有幫助, 感謝 下方點贊 或 Star GitHub: sequelize-docs-Zh-CN 支持, 謝謝.

相關文章
相關標籤/搜索