2016年9月12日 星期一

MongoDB進階教學

用 MongoDB 開發程式也面臨到許多難題,雖然mongo是適用物件儲存時非常具有彈性,但也有它的缺點,例如型別檢查是非常不嚴格的,如果資料欄位型態儲存失敗,也不會有任何的提示訊息,需要特別注意,因為MongoDB不支援某些SQL的型別,所以要先轉換型別,所以在LINQ上需要轉換型別時,可以參考以下的範例

var oders = (from b in realTimeDb.v_orderinfo4back
select new
{
CREATEDDATE = b.CREATEDDATE,
COLORNAME = b.colorname,
COST = b.COST,
deliverytypename = b.deliverytypename,
ITEMID = b.ITEMID,
}).AsEnumerable().Select(q=> new vOrderInfo4Back() {
CREATEDDATE = q.CREATEDDATE,
COLORNAME = q.COLORNAME,
COST = Convert.ToDouble(q.COST),
DELIVERYTYPENAME =q.deliverytypename,
ITEMID = Convert.ToDouble(q.ITEMID),
});
注意: SQLDeciaml型別,但mongodb 並沒有deciaml會被轉換成string,所以mongodb在型態檢查是不嚴格

建立mongoDB的索引
//-----add index------//
var keyCreateDates = MongoDB.Driver.Builders<vOrderInfo4Back>.IndexKeys.Ascending("CREATEDDATE");
_db.vOrderInfo4Back.Indexes.CreateOne(keyCreateDates);

var keyOrderid = MongoDB.Driver.Builders<vOrderInfo4Back>.IndexKeys.Ascending("ORDERID");
_db.vOrderInfo4Back.Indexes.CreateOne(keyOrderid);

var keySALENO = MongoDB.Driver.Builders<vOrderInfo4Back>.IndexKeys.Ascending("SALENO");
_db.vOrderInfo4Back.Indexes.CreateOne(keySALENO);


注意: MongoDB 會自動新增Collection 【如同SQL的資料表】,並不需要事先開table 儲存資料,所以如果要在程式中建立索引亦可以加快搜尋速度。

建立mongoDB的儲存模型
public class vOrderInfo4Back {
/// <summary>
/// 訂單日期
/// </summary>
public DateTime? CREATEDDATE { get; set; }
/// <summary>
/// 訂單編號
/// </summary>
public double ORDERID { get; set; }
/// <summary>
/// 項次
/// </summary>
public double ITEMID { get; set; }

/// <summary>
/// 顏色
/// </summary>
public string COLORNAME { get; set; }

/// <summary>
/// 成本
/// </summary>
public double? COST { get; set; }

/// <summary>
/// 配送方式
/// </summary>
public string DELIVERYTYPENAME { get; set; }
}
因為MongoDB十分有彈性,只有在儲存的model 增加欄位,儲存至mongoDB會自動增加欄位,並不用特別針對資料庫增加欄位,這點是NO SQL 的好處之一,只需著重程式開發上是否會影響之前的程式。

因為MongoDB 讀取時間會依預設的標準時間為主(US),所以要加上BsonDateTimeOptions 關鍵字 或者 註明當地時間 [BsonDateTimeOptions(kind=dateTimekind.Local)]
/// <summary>
/// 訂單日期
/// </summary>
[BsonDateTimeOptions]
public DateTime CREATEDDATE { get; set; }


注意:如果要讀取MongoDB日期欄位,需要加註 [BsonDateTimeOptions]在欄位的上方

單筆寫入MongoDB的範例:
var _db = new MallDbContext(); //是自己新增的物件,用來取得MongoDB的資料庫
_db.vOrderInfo4Back.InsertOne(new vOrderInfo4Back() {
COLORNAME = "",
COST=0,
ITEMID=1,
SALENO=1,
DELIVERYTYPENAME="測試"
});
_db.vOrderInfo4Back : 自己新增的物件回傳vorderInfo4Backcollection

如果有多筆資料要一次寫入MongoDB時,需要用InsertMany 可以加快mongo寫入的速度
_db.vOrderInfo4Back.InsertMany(orderList);

查詢MonoLINQ語法
var _db = new MallDbContext();
var vOrders=
_db.GetCollection<vOrderInfo4Back>("vOrderInfo4Back").AsQueryable<vOrderInfo4Back>();
//支援LINEQ語法
var orders = from q in vOrders where (string.IsNullOrEmpty(inputModel.productName) || q.SALENAME.Contains(inputModel.productName ?? ""))
&& q.CREATEDDATE >= startDate
&& q.CREATEDDATE <= endDate
&& q.SUPPLIERID == doubleFugoID
orderby q.CREATEDDATE descending
select q;


C#查詢Mongo原生語法
var builder = Builders<vOrderInfo4Back>.Filter;
var filter = builder.Gte(q => q.CREATEDDATE, startDate) & builder.Lte(q => q.CREATEDDATE, endDate) //GTE 如同>=LTE 如同<=

//商品名稱 (LIKE '% productName%' )
if (!string.IsNullOrWhiteSpace(inputModel.productName))
{
filter = filter & builder.Regex(q => q.SALENAME, "/.*" + inputModel.productName + ".*/");
}
// supplier
if (!string.IsNullOrWhiteSpace(fugoID))
{
filter = filter & builder.Eq(q => q.SUPPLIERID, doubleFUgoID);
}

//前台分類
if (categories2.Count > 0)
{
filter = filter & builder.In(q => q.SCLASSCODE, categories2);
}
var vOrders = _db.GetCollection<vOrderInfo4Back>("vOrderInfo4Back").Find(filter)

以上是我對MongoDB的基本瞭解,如果有任何補充的地方,還希望大家多多指教~ 感謝大家

2016年8月9日 星期二

Ionic2 藍芽傳輸

目前藍芽推出BLE低功率的傳輸模式,當然對藍芽有興趣的朋友們,可以一起來研究看看喔,和傳統藍芽不一樣的是採用藍芽4.0的標準,以傳統藍芽更省電,更有延展性傳輸多樣化的訊息,距離也比傳統藍芽更遠,其他就自已有空去GOOLE~

一、加入BLE的套件

$ ionic plugin add cordova-plugin-ble-central

二、搜尋藍芽的硬體

home.html
<ion-navbar *navbar>
<ion-title>藍芽裝置</ion-title>
</ion-navbar>

<ion-content padding class="home">
<ion-nav #mycontent [root]="rootPage"></ion-nav>
<ion-list inset>
<ion-item-sliding *ngFor="let device of devices" #slidingItem>
<button ion-item (click)="connectToDevice(device)">
<h2>{{device.name}}</h2>
<p>{{device.id}}</p>
<p>{{device.rssi}}</p>
</button>
</ion-item-sliding>
</ion-list>
<button full (click)="startScanning()">開始掃瞄</button>
</ion-content>


Home.ts
import {Component} from '@angular/core';
import {Page, NavController} from 'ionic-angular';
import {Device} from './device'; //呼叫元件
import {BLE} from 'ionic-native';

@Component({
templateUrl: 'build/pages/home/home.html'
})

export class HomePage {
private devices;
private isScanning = false;
constructor(public navController: NavController) {
this.navController = navController;
this.devices = [];
}

startScanning() {
console.log("Scanning Started");
this.devices = [];
this.isScanning = true;
BLE.enable(); //開啟藍芽的授權
BLE.startScan([]).subscribe(device => {
this.devices.push(device);
});

setTimeout(() => {
BLE.stopScan().then(() => {
console.log("Scanning has stopped");
console.log(JSON.stringify(this.devices))
this.isScanning = false;
});
}, 3000);
}

connectToDevice(device) {
console.log("Connect To Device");
console.log(JSON.stringify(device))
this.navController.push(Device, {
device: device
}); //轉頁到device的頁面
}
}

設定3秒後自動停止搜尋,如果有搜尋到配對的藍芽裝置就加入device的陣列中

Device.html
<ion-navbar *navbar>
<ion-title>{{device.name}}</ion-title>
</ion-navbar>
<ion-content>
<ion-list inset>
<ion-item-sliding *ngFor="let characteristic of characteristics" #slidingItem>
<button ion-item (click)="connectToCharacteristic(device.id,characteristic)">
<h2>{{characteristic.characteristic}}</h2>
<p>{{characteristic.properties}}</p>
<p>{{characteristic.isNotifying}}</p>
<p>{{characteristic.service}}</p>
</button>
</ion-item-sliding>
</ion-list>
<ion-spinner *ngIf="connecting ==true" name="circles"></ion-spinner>
</ion-content>

點選配對的藍芽裝置後,導到Device.html的頁面,並顯示藍芽配備的相關屬性

Device.ts

import {Page, NavController, NavParams} from "ionic-angular";
import {BLE} from "ionic-native";
import {Component} from '@angular/core';
@Component({
templateUrl: "build/pages/home/device.html"
})

export class Device {
static get parameters() {
return [[NavParams], [NavController]];
}
private nav;
private navParams;
private device;
private connecting;
private characteristics;
constructor(navParams, nav) {
this.nav = nav;
this.navParams = navParams;
this.device = this.navParams.get("device");
this.connecting = true;
this.connect(this.device.id);
}

connect(deviceID) {
this.characteristics = [];
BLE.connect(deviceID).subscribe(peripheralData => {
console.log(peripheralData.characteristics);
this.characteristics = peripheralData.characteristics;
this.connecting = false;
},
peripheralData => {
console.log("disconnected");
});
}

stringToBytes(string) {
var array = new Uint8Array(string.length);
for (var i = 0, l = string.length; i < l; i++) {
array[i] = string.charCodeAt(i);
}
return array.buffer;
}

bytesToString(buffer) {
return String.fromCharCode.apply(null, new Uint8Array(buffer));
}

connectToCharacteristic(deviceID, characteristic) {
alert(deviceID + "_" + characteristic);
console.log("Connect To Characteristic");
console.log(deviceID);
console.log(characteristic);
//you can write or read message
}
}



BLE的屬性如下所示:

"characteristics":
[
{ "service": "1800",
"characteristic": "2a00",
"properties": [ "Read" ]
}
]

如果要傳輸或接受藍芽的訊息,必需要取得deviceID, serviceID, characteristicID,而有些屬性只允許【唯讀】、【唯寫】,必需先瞭解裝備的的屬性。

write(deviceId, serviceUUID, characteristicUUID, value)

read(deviceId, serviceUUID, characteristicUUID)



參考文件
  1. https://ionicallyspeaking.com/2016/05/06/connecting-to-a-bluetooth-smart-sensor-with-ionic-2/
  2. http://ionicframework.com/docs/v2/native/ble/