The unsafe resource URL and cross origin on angular

在angular很常遇到compile template時出錯的情況,因為在angular中,使用比較嚴謹的判斷方式,只要看到不符合規範,會直接拋出exception

##Cross origin

以下是一個video的範例,設定影片的source:

1
2
3
<video width="320" height="240" controls>
<source src="{{source}}" type="video/mp4">
</video>

設定source的影片來源:

1
$scope.source = "http://other.sparrowhome.twbbs.org/movie.mp4"

這時就會發現angular拋出exception,出現cross domain的問題。

但是若是直接寫在html上呢?

1
2
3
<video width="320" height="240" controls>
<source src="http://other.sparrowhome.twbbs.org/movie.mp4" type="video/mp4">
</video>

此時你可以發現能正常的播放,但是在angular就是會拋錯,這時你可以透過$sceDelegateProvider,來設定黑名單白名單,決定哪個domain可以讀取這隻影片。

假設你要讓所有domain都能使用,在白名單可以這樣寫:

1
2
3
4
5
angular
.module('MyApp')
.config(['$sceDelegateProvider', function($sceDelegateProvider) {
$sceDelegateProvider.resourceUrlWhitelist(['**']);
})

若是你只要目前網域底下的子網域都可以讀取的話,可以這樣寫:

1
$sceDelegateProvider.resourceUrlWhitelist(['http://*.sparrowhome.twbbs.org'])

如果要透過黑名單可以直接使用resourceUrlBlacklist

##Unsafe resource URL

如果是讀取img,不管是cross domain都能正常讀取,不過還是會有不讀取的狀況。

例如你透過一個<input type="file">,去取得顯示image,像是<img ng-src="{{img}}" />

1
$scope.img = URL.createObjectURL(imgFile);

此時就會拋出unsafe:blobexception,依據URL.createObjectURL所取得url,可以很容易發現,最前方協定是不同的。例如URL.createObjectURL回傳一個blob:http%3A%2F%2Fsparrowhome.twbbs.org%2Fimage.png,此時不在預設的允許名單中,這時就要透過$compileProvider.imgSrcSanitizationWhitelist去改寫。

像是讓所有協定都通過:

1
2
3
4
angular.module('MyApp').config(['$sceDelegateProvider', function( $compileProvider ) {

$compileProvider.imgSrcSanitizationWhitelist(/^.*/);
}

也可自訂要通過特定的協定,像是/^(https?|ftp|file|blob|data):/,同樣的href也有aHrefSanitizationWhitelist可以去設定。在angular中使用要嚴謹的判斷方式,當然也可透過jquery或原生javascript繞過這問題,不過這樣就有點失去用angular的意義了,畢竟用template的方式,比起寫code跟為簡潔。

Neo4j - Transactional HTTP endpoint

要讀取、更新、刪除和建立neo4j的node或relationship,可以透過embedded或http的方式,去針對database去操作。

使用http的方式,基本上就是呼叫rest api,db server會依照不同的狀況回傳response status code,同樣也會依照get、post、put、delete去告訴db server,目前要處理方式。

然而neo4j中,提供了很多http的api,可以針對db操作,像是rest api transactionalCypher這類的neo4j rest api

Embedded graph database

透過embedded的方式,會直接在嵌入在你所撰寫的project裡,指定好要儲存的db的path,換句話說,檔案會儲存在server的local端,與以往的mysql、mongodb透過socket的方式不同,有點類似sqlite。

使用embedded的方式,大概會如下方(參考官方):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
GraphDatabaseService graphDb = new GraphDatabaseFactory().newEmbeddedDatabase( "/your_db_path" );

try {
Transaction tx = graphDb.beginTx();

Node firstNode = graphDb.createNode();
firstNode.setProperty( "message", "Hello world" );
System.out.print( firstNode.getProperty( "message" ) );

// Database operations go here
tx.success();

}catch( Exception e ){

//do something
}

Transactional HTTP endpoint

在這簡單介紹Transactional HTTP endpoint,首先neo4j的每個query都會經由transaction機制去處理,所以可以透過已知的transactionid,下一整串的query,最後在決定要rollbackcommit,如果太久沒有commit,neo4j會有個timeout時間(預設60 sec),就會取消先前的query結果。

Begin a transaction

建立一個transaction,之後可以沿用這個transactionid,去執行一連串的query。

官方Example:

Request headers:

1
2
3
POST http://localhost:7474/db/data/transaction
Accept: application/json; charset=UTF-8
Content-Type: application/json

Request json data:

1
2
3
4
5
6
7
8
9
10
{
"statements" : [ {
"statement" : "CREATE (n {props}) RETURN n",
"parameters" : {
"props" : {
"name" : "My Node"
}
}
} ]
}

statements可以一次下多筆query,statement會參照parameters的參數。

Response headers:

1
2
3
201: Created
Content-Type: application/json
Location: http://localhost:7474/db/data/transaction/7

Response json data:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"commit" : "http://localhost:7474/db/data/transaction/7/commit",
"results" : [ {
"columns" : [ "n" ],
"data" : [ {
"row" : [ {
"name" : "My Node"
} ]
} ]
} ],
"transaction" : {
"expires" : "Mon, 03 Feb 2014 13:26:48 +0000"
},
"errors" : [ ]
}

每個response都會回傳一個transactionexpires,這個expires的指的就是transaction的存活時間,而commit這個field包含transactionid,這上面這範例id為7。

Execute statements in an open transaction

使用先前建立transaction,依照transactionid,繼續執行query,若是transaction失效,最後會取得到error messsage。

官方Example:

Request headers:

1
2
3
POST http://localhost:7474/db/data/transaction/7
Accept: application/json; charset=UTF-8
Content-Type: application/json

Request json data:

1
2
3
4
5
{
"statements" : [ {
"statement" : "CREATE n RETURN n"
} ]
}

Response headers:

1
2
200: OK
Content-Type: application/json

Response json data:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"commit" : "http://localhost:7474/db/data/transaction/7/commit",
"results" : [ {
"columns" : [ "n" ],
"data" : [ {
"row" : [ {
} ]
} ]
} ],
"transaction" : {
"expires" : "Mon, 03 Feb 2014 13:26:48 +0000"
},
"errors" : [ ]
}

Execute statements in an open transaction in REST format for the return

在還沒commit之前,只有node會被建立,而建立的propertiesrelationship等的…,一直到commit之後,在database中才有辦法query的到。而REST這個參數,可以幫助你取得目前node間的狀況,response會回傳各種想要查詢的rest api。

官方Example:

Request headers:

1
2
3
POST http://localhost:7474/db/data/transaction/1
Accept: application/json; charset=UTF-8
Content-Type: application/json

Request json data:

1
2
3
4
5
6
{
"statements" : [ {
"statement" : "CREATE n RETURN n",
"resultDataContents" : [ "REST" ]
} ]
}

Response headers:

1
2
200: OK
Content-Type: application/json

Response json data:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{
"commit" : "http://localhost:7474/db/data/transaction/1/commit",
"results" : [ {
"columns" : [ "n" ],
"data" : [ {
"rest" : [ {
"paged_traverse" : "http://localhost:7474/db/data/node/12/paged/traverse/{returnType}{?pageSize,leaseTime}",
"labels" : "http://localhost:7474/db/data/node/12/labels",
"outgoing_relationships" : "http://localhost:7474/db/data/node/12/relationships/out",
"traverse" : "http://localhost:7474/db/data/node/12/traverse/{returnType}",
"all_typed_relationships" : "http://localhost:7474/db/data/node/12/relationships/all/{-list|&|types}",
"property" : "http://localhost:7474/db/data/node/12/properties/{key}",
"all_relationships" : "http://localhost:7474/db/data/node/12/relationships/all",
"self" : "http://localhost:7474/db/data/node/12",
"properties" : "http://localhost:7474/db/data/node/12/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/12/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/12/relationships/in",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/12/relationships/in/{-list|&|types}",
"create_relationship" : "http://localhost:7474/db/data/node/12/relationships",
"data" : {
}
} ]
} ]
} ],
"transaction" : {
"expires" : "Mon, 03 Feb 2014 13:26:44 +0000"
},
"errors" : [ ]
}

Reset transaction timeout of an open transaction

transaction是會過期的,要解決這問題,只要向http://localhost:7474/db/data/transaction/1發送,時效性就會自動延長,若不想下query,只想延長時效。可以發送一個不帶statements內容的的query。

Request headers:

1
2
3
POST http://localhost:7474/db/data/transaction/1
Accept: application/json; charset=UTF-8
Content-Type: application/json

Request json data:

1
2
3
{
"statements" : [ ]
}

Commit an open transaction

要確定存入database,只要commit目前的transaction即可。

Request:

1
2
3
POST http://localhost:7474/db/data/transaction/4/commit
Accept: application/json; charset=UTF-8
Content-Type: application/json

Rollback an open transaction

要取消之前transaction中執行過的query,只要使用rollback即可。

Request:

1
2
DELETE http://localhost:7474/db/data/transaction/3
Accept: application/json; charset=UTF-8

Begin and commit a transaction in one request

如果要執行query後,馬上commit,可以直接透過/db/data/transaction/commit

Request headers:

1
2
3
POST http://localhost:7474/db/data/transaction/commit
Accept: application/json; charset=UTF-8
Content-Type: application/json

Request json data:

1
2
3
4
5
{
"statements" : [ {
"statement" : "CREATE n RETURN id(n)"
} ]
}

Return results in graph format

如果想要查詢節點的圖形結構關係,可以在resultDataContents加入graph這個參數。

Request headers:

1
2
3
POST http://localhost:7474/db/data/transaction/commit
Accept: application/json; charset=UTF-8
Content-Type: application/json

Request json data:

1
2
3
4
5
6
{
"statements" : [ {
"statement" : "CREATE ( bike:Bike { weight: 10 } )CREATE ( frontWheel:Wheel { spokes: 3 } )CREATE ( backWheel:Wheel { spokes: 32 } )CREATE p1 = bike -[:HAS { position: 1 } ]-> frontWheel CREATE p2 = bike -[:HAS { position: 2 } ]-> backWheel RETURN bike, p1, p2",
"resultDataContents" : [ "row", "graph" ]
} ]
}

Response headers:

1
2
200: OK
Content-Type: application/json

Response json data:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
{
"results" : [ {
"columns" : [ "bike", "p1", "p2" ],
"data" : [ {
"row" : [ {
"weight" : 10
}, [ {
"weight" : 10
}, {
"position" : 1
}, {
"spokes" : 3
} ], [ {
"weight" : 10
}, {
"position" : 2
}, {
"spokes" : 32
} ] ],
"graph" : {
"nodes" : [ {
"id" : "17",
"labels" : [ "Wheel" ],
"properties" : {
"spokes" : 3
}
}, {
"id" : "16",
"labels" : [ "Bike" ],
"properties" : {
"weight" : 10
}
}, {
"id" : "18",
"labels" : [ "Wheel" ],
"properties" : {
"spokes" : 32
}
} ],
"relationships" : [ {
"id" : "9",
"type" : "HAS",
"startNode" : "16",
"endNode" : "17",
"properties" : {
"position" : 1
}
}, {
"id" : "10",
"type" : "HAS",
"startNode" : "16",
"endNode" : "18",
"properties" : {
"position" : 2
}
} ]
}
} ]
} ],
"errors" : [ ]
}


Python example

上述對照的request json datarequest headers可參考底下的範例,每個語言使用方式差不多。

dependency

install

1
sudo pip install requests

code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import requests, json       

headers = {
"Accept": "application/json; charset=UTF-8",
"Content-Type": "application/json"
}

data = {
"statements" : [ {
"statement" : "MATCH (n {name: {name}}) RETURN n",
"parameters" : {
"name":"Sparrow"
}
} ]
}

url = "http://localhost:7474/db/data/transaction"

req = requests.post( url , data = json.dumps( data ), headers = headers )

print req.text

可參照官方的Transactional HTTP endpoint,和rest api回傳的status code

Neo4j - Getting started

neo4j relationship pic

neo4j是一種圖形資料庫,同時也是nosql的一種,每筆資料為一個node,在node上可以定義label,類似於其他資料庫的table/collection。假設有個Person的collection,要定義PersonPerson之間的關係,勢必得額外定義一個collection或者field,去描述PersonPerson之間的關係,但是neo4j並不需要額外的collection或field,只要定義好Person這個label,當關係產生時,在額外描述PersonPerson之間關係(relationship)為何,換句話說一開始建立Person的node時,並不需要定義PersonPerson的關係。

舉例來說,如果今天Tom和Jack就讀同一個學校同一個班級,現在的關係是classmate,當熟識之後,這時關係可能是friend,這時就必須建立friend的關聯(朋友本身是雙向的,這邊以單向為例)。

tom-->jack

在neo4j建立Persontypefriend關聯的語法會如下:

1
CREATE (Tom {name:"tom"})-[:friend]->(Jack {name:"jack"});

如果單純的建立relationship,需先抓取tom和jack兩個人:

1
2
3
4
MATCH (tom), (jack)
WHERE tom.name = "tom" and jack.name = "jack"
CREATE (tom)-[:friend]->(jack)
RETURN tom,jack;

Neo4j中的Nodes

在neo4j中的node,可以寫成以下幾種方式:

  • (a)
  • (a:Person) 指定label為Person
  • () 以匿名的方式

括號中的a,如同(),唯一不同之處,可以在MATCH時,透過a來做比對,而透過:Person這種指定label的方式,可以明確要查詢的label名稱,同樣也會增加query的效率。

Neo4j中的Relationships

在neo4j中的關係(relationship)表示方式,以中括弧[]作為表示,可以參考以下幾種:

  • (a)-[r]->(m) 定義a點到m點之間的關係
  • (a)-[:ACTED_IN]->(m) 定義a點到m點之間的關係,其中定義:ACTED_IN的label,為描述a與m之間的關係

Labels

不管是noderelationship都可以定義一個甚至多個label。下方以定義一個label為例:

  • (a:Person)
  • (a:Person {name:"Keanu Reeves"})
  • (a:Person)-[:ACTED_IN]->(m:Movie)

CREATE

建立一個node,label為Person{name:"tom"}為node被建立的資料。最前面的tom只是一個變數,在這個語法中並不會被使用到。

1
CREATE (tom:Person {name:"tom"});

建立classmaterelationship,其中classmaterelationship中,被稱為type

1
CREATE (Tom {name:"tom"})-[:classmate]->(Jack {name:"jack"});

如果要描述關係的其他屬性,例如什麼時間點成為classmate,可在關係中附加資料。

1
CREATE (Tom {name:"tom"})-[:classmate {start_date:"2012/01/05"}]->(Jack {name:"jack"});

CREATE INDEX

依照name去建立index,在search中效率會提昇許多。

1
CREATE INDEX ON :Person(name);

MATCH

如果要回傳所有node筆數的資料如下。

1
MATCH (p) RETURN p;

找出有建立relationship的node,可以這樣寫。

1
MATCH (t)-->(j) RETURN t,j;

若是要依照type去search,例如要找是friend關係的,列出Person的name。

1
MATCH (t)-[:friend]->(j) RETURN t.name,j.name;

查詢PersonPerson之間的關係,可以透過type這個function。

1
MATCH (t)-[r]->(j) RETURN t.name, type( r ),j.name;

WHERE

要search某個屬性,可透過where,例如找name是tom的人。

1
2
3
MATCH (t)
WHERE t.name = "tom"
RETURN t;

同樣的查詢條件,也可直接透過下面這種方式,寫法更為簡短。

1
2
MATCH (t {name:"tom"})
RETURN t;

Order

依照出生日期作排序。

1
2
3
MATCH (a:Person)
RETURN a.name, a.born
ORDER BY a.born

Limit and Skip

limit指的是query後的資料要抓幾筆,而skip則是從第幾筆開始抓取。

從第10筆開始抓取,最多抓10筆。

1
2
3
4
MATCH (a)
RETURN a.name
SKIP 10
LIMIT 10;

nodes

在path中,回傳所有節點。

1
MATCH p = (t)-[:friend]->(j) RETURN nodes(p);

rels

在path中,回傳節點對應的關係。

1
MATCH p = (t)-[:friend]->(j) RETURN rels(p);

DISTINCT

假設有兩個Person的名稱為相同,此時要列出所有Person的name,這時可透過DISTINCT把相同name的都過濾掉,

1
2
MATCH (p:Person)
RETURN DISTINCT p;

SET

SET可用於noderelationshiplabel的屬性修改。

例如例如例如例如設定movie釋出時間。

1
2
3
4
MATCH (movie:Movie)
WHERE movie.title="Mystic River"
SET movie.released = 2003
RETURN movie;

如果要加入一個label,可直接使用SET

1
2
3
4
MATCH (movie:Movie)
WHERE movie.title="Mystic River"
SET movie:Movie
RETURN movie

REMOVE

移除label,直接使用REMOVE即可。

1
2
3
4
MATCH (movie:Movie)
WHERE movie.title="Mystic River"
REMOVE movie:Movie
RETURN movie

DELETE

刪除noderelationship

刪除一個節點。

1
2
MATCH (n { name: 'Peter' })
DELETE n

MATCH Depth Relationship

a到b所包含多個relationship

1
(a)-[*]->(b)

a到b包含1~3個relationship,也就是中間含有0~3個node。

1
(a)-[*1..4]->(b)

shortestPath

會自動找出最短路徑,也就是假設a到b點,可能有10個路徑,只會傳一個最短路徑。

1
2
3
4
MATCH (keanu:Person {name:"Keanu Reeves"}), 
(kevin:Person {name:"Kevin Bacon"})
MATCH p=shortestPath((keanu)-[:KNOWS*]->(kevin))
RETURN p;

extract

可在extract執行運算,之後的結果會回傳一個list。寫法包括以下2種:

  • extract(n in nodes(path) | n.name)
  • [ x in coll | expr ]
1
extract(x in [1,2,3] | x*x)

取得keanu知道(relationship)kevin的最短路徑,其中[1..-1]則捨棄list的第一個和最後一個值。

1
2
3
4
MATCH (keanu:Person {name:"Keanu Reeves"}), 
(kevin:Person {name:"Kevin Bacon"})
MATCH p=shortestPath((keanu)-[:KNOWS*]->(kevin))
RETURN [ n in nodes(p)[1..-1] | n.name ];

neo4j可用的function可以參考官方文件。另外可以參考官方的語法對照表官方的online tutorial,對於neo4j學習會有很大幫助。