這里記錄一個查詢需求:數據庫中字段的值(數組類型)都在指定的數組中。舉例說一下實際場景,數據庫中一個字段存儲用戶“可以使用的編程語言”,一般都會是多個,所以該字段是數組格式。現在要查詢的是:會c#、javascript或者只會c#或者只會javascript的用戶,翻譯一下就是數據庫中字段的值是子集而給定的數組是全集。這種查詢需要在mongodb沒有找到特定的查詢操作符,這篇筆記主要解決這個問題,順便介紹一下"$all"運算符。"$all"查詢的是數據庫字段的值包含所有指定元素的數組,也就是數據庫中字段的值是全集而給定的數組是子集,和前面提到的需求相反。
為了演示上述的兩種查詢需求,先造一些測試數據,下面是表結構:
| 編程語言調查表(FormId: 507048044944694000, FormVersion: 507048044944694001) |
||||
| 唯一標識 |
中文描述 |
控件類型 |
是否必填 |
表單項的其他配置(在表單設計時配置,文本框長度、時間格式等) |
| 1572493554001 |
用戶 |
選擇人員控件 |
是 |
|
| 1572493554002 |
可以使用的編程語言 |
復選框 |
否 |
|
| 1572493554003 |
最喜歡的編程語言 |
文本框 |
否 |
|
| 1572493554004 |
工作地點 |
文本框 |
否 |
|
| 1572493554005 |
工作年限 |
數值輸入框 |
否 |
|
| 1572493554006 |
備注 |
多行文本框 |
否 |
|
下面是造數據的語句
var GV_TableName = "FormInstace", GV_FormId = "507048044944694000", GV_FormVersion = "507048044944694001", GV_CreateUserIds = ["user10000", "user10001", "user10002", "user10003", "user10004", "user10005", "user10006", "user10007", "user10008", "user10009"]; var GV_LangObj = { 1: { id: "1", text: "C#" }, 2: { id: "2", text: "JavaScript" }, 3: { id: "3", text: "HTML" }, 4: { id: "4", text: "CSS" }, 5: { id: "5", text: "Go" }, 6: { id: "6", text: "Rust" } }; var GV_Name2Id = { "userName": "1572493554001", "lang": "1572493554002", "favLang": "1572493554003", "workPlace": "1572493554004", "workYears": "1572493554005", "remarks": "1572493554006", }; var getGUID = function () { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); return v.toString(16).toUpperCase(); }); } var getFormInstanceOtherAttrs = function (formId, formVersion) { var tempCreateUserIdIndex = Math.floor(Math.random() * GV_CreateUserIds.length), tempCreateDate = ISODate(); return { _id: getGUID(), ExtendData: {}, CreateUserId: GV_CreateUserIds[tempCreateUserIdIndex], CreateUserName: GV_CreateUserIds[tempCreateUserIdIndex], CreateDate: tempCreateDate, LastModifyDate: tempCreateDate, FormId: GV_FormId, FormVersion: GV_FormVersion }; }; var assembleFormInstance = function (formItemsAttr) { return Object.assign(formItemsAttr, getFormInstanceOtherAttrs()); } //************************************************************************************************************************************************ // 批量插入數據 db[GV_TableName].insertMany([ assembleFormInstance({ FormItems: [{ key: GV_Name2Id.userName, value: "u1" }, { key: GV_Name2Id.lang, value: [GV_LangObj['1'], GV_LangObj['2'], GV_LangObj['3'], GV_LangObj['4']] }, { key: GV_Name2Id.favLang, value: [GV_LangObj['1']] }, { key: GV_Name2Id.workPlace, value: "北京" }, { key: GV_Name2Id.workYears, value: 1 }, { key: GV_Name2Id.remarks, value: "隨便寫點什么" } ] }), assembleFormInstance({ FormItems: [{ key: GV_Name2Id.userName, value: "u2" }, { key: GV_Name2Id.lang, value: [GV_LangObj['1'], GV_LangObj['2'], GV_LangObj['6']] }, { key: GV_Name2Id.favLang, value: [GV_LangObj['6']] }, { key: GV_Name2Id.workPlace, value: "天津" }, { key: GV_Name2Id.workYears, value: 2 }, { key: GV_Name2Id.remarks, value: "隨便寫點什么" } ] }), assembleFormInstance({ FormItems: [{ key: GV_Name2Id.userName, value: "u3" }, { key: GV_Name2Id.lang, value: [GV_LangObj['1'], GV_LangObj['2']] }, { key: GV_Name2Id.favLang, value: [GV_LangObj['1']] }, { key: GV_Name2Id.workPlace, value: "石家庄" }, { key: GV_Name2Id.workYears, value: 3 }, { key: GV_Name2Id.remarks, value: "隨便寫點什么" } ] }), assembleFormInstance({ FormItems: [{ key: GV_Name2Id.userName, value: "u4" }, { key: GV_Name2Id.lang, value: [GV_LangObj['1'], GV_LangObj['5']] }, { key: GV_Name2Id.favLang, value: [GV_LangObj['5']] }, { key: GV_Name2Id.workPlace, value: "上海" }, { key: GV_Name2Id.workYears, value: 4 }, { key: GV_Name2Id.remarks, value: "隨便寫點什么" } ] }), assembleFormInstance({ FormItems: [{ key: GV_Name2Id.userName, value: "u5" }, { key: GV_Name2Id.lang, value: [GV_LangObj['1']] }, { key: GV_Name2Id.favLang, value: [GV_LangObj['1']] }, { key: GV_Name2Id.workPlace, value: "廣州" }, { key: GV_Name2Id.workYears, value: 5 }, { key: GV_Name2Id.remarks, value: "隨便寫點什么" } ] }), assembleFormInstance({ FormItems: [{ key: GV_Name2Id.userName, value: "u6" }, { key: GV_Name2Id.lang, value: [GV_LangObj['2']] }, { key: GV_Name2Id.favLang, value: [GV_LangObj['2']] }, { key: GV_Name2Id.workPlace, value: "深圳" }, { key: GV_Name2Id.workYears, value: 6 }, { key: GV_Name2Id.remarks, value: "隨便寫點什么" } ] }), assembleFormInstance({ FormItems: [{ key: GV_Name2Id.userName, value: "u7" }, { key: GV_Name2Id.lang, value: [] }, { key: GV_Name2Id.favLang, value: [] }, { key: GV_Name2Id.workPlace, value: "成都" }, { key: GV_Name2Id.workYears, value: 7 }, { key: GV_Name2Id.remarks, value: "隨便寫點什么" } ] }), assembleFormInstance({ FormItems: [{ key: GV_Name2Id.userName, value: "u8" }, { key: GV_Name2Id.lang, value: [GV_LangObj['5'], GV_LangObj['6']] }, { key: GV_Name2Id.favLang, value: [GV_LangObj['5']] }, { key: GV_Name2Id.workPlace, value: "重慶" }, { key: GV_Name2Id.workYears, value: 8 }, { key: GV_Name2Id.remarks, value: "隨便寫點什么" } ] }), ]);
看一下插入的數據:

這里數據結構和之前表單生成器(Form Builder)之表單數據存儲結構mongodb篇文章中介紹的一樣。為了方便查看,將“可以使用的編程語言”字段從“FormItems”數組中拿出來並放在最外層,下面是語句
// 通用聚合管道(將“編程語言”表單項從"FormItems"中拷貝一份放到最外層,方便查看) var showLangItemPrePipeline = [{ $addFields: { FormItemObj: { $arrayToObject: { $map: { input: "$FormItems", as: "field", in: [ "$$field.key", "$$field.value" ] } } } } }, { $addFields: { "LangFormItem": "$FormItemObj.1572493554002", } }, { $addFields: { "1572493554002": { $reduce: { input: "$LangFormItem", initialValue: "", in: { $concat: ["$$value", "$$this.text", ","] } } }, } }, { $project: { 'FormItemObj': 0, 'LangFormItem': 0 } } ]; // 1、查詢:展示一下插入的示例數據 db.getCollection("FormInstace").aggregate(showLangItemPrePipeline);
下面看一下查詢效果:

說明:從制造假數據的語句中你可以看到“1572493554002”字段是數組類型並且每一項都是一個對象,上圖中將數組拼接成了字符串,方便查看。
先來看一下數據庫中字段的值都在指定元素的數組中的查詢語句:
// 通用聚合管道(將“編程語言”表單項從"FormItems"中拷貝一份放到最外層,方便查看) var showLangItemPrePipeline = [{ $addFields: { FormItemObj: { $arrayToObject: { $map: { input: "$FormItems", as: "field", in: [ "$$field.key", "$$field.value" ] } } } } }, { $addFields: { "LangFormItem": "$FormItemObj.1572493554002", } }, { $addFields: { "1572493554002": { $reduce: { input: "$LangFormItem", initialValue: "", in: { $concat: ["$$value", "$$this.text", ","] } } }, } }, { $project: { 'FormItemObj': 0, 'LangFormItem': 0 } } ]; db.getCollection("FormInstace").aggregate(showLangItemPrePipeline.concat([{ "$match": { "FormId": "507048044944694000", "FormItems": { "$elemMatch": { "key": "1572493554002", "value.0": { '$exists': true }, "value": { "$not": { "$elemMatch": { "text": { "$nin": ["C#", "JavaScript"] } } } } } } } } ]))
來一張截圖,看一下查詢結果:

注意:這里語句中還用到了“$exists”運算符,如果不添加這個會將數組長度為0的查出來。參考鏈接。
在看一下“$all”查詢,數據庫字段的值包含所有指定元素的數組:
// 通用聚合管道(將“編程語言”表單項從"FormItems"中拷貝一份放到最外層,方便查看) var showLangItemPrePipeline = [{ $addFields: { FormItemObj: { $arrayToObject: { $map: { input: "$FormItems", as: "field", in: [ "$$field.key", "$$field.value" ] } } } } }, { $addFields: { "LangFormItem": "$FormItemObj.1572493554002", } }, { $addFields: { "1572493554002": { $reduce: { input: "$LangFormItem", initialValue: "", in: { $concat: ["$$value", "$$this.text", ","] } } }, } }, { $project: { 'FormItemObj': 0, 'LangFormItem': 0 } } ]; db.getCollection('FormInstace').aggregate(showLangItemPrePipeline.concat([{ "$match": { "FormId": "507048044944694000", "FormItems": { "$elemMatch": { "key": "1572493554002", "value.text": { "$all": ["C#", "JavaScript"] } } } } } ]))
來一張截圖,看一下查詢結果:

這里在順便記錄一下在mongodb中數值轉字符串,高版本有“$toString”操作符(版本4.0)、“$convert”操作符(版本4.0)……但是低版本的該如何處理,參考鏈接。這個例子比較簡單,就不寫制造數據的語句了,直接來查詢語句:
db.getCollection('test001').aggregate([
{
$addFields: {
"ageStr": { $substr: [ "$num", 0, -1 ] }
}
}
])
來一張截圖,看一下查詢結果:

