Git rebase 實戰演練

自從使用Git之後,因為開始變得我天不怕地不怕
有時候上個廁所也好,出門覓個食也罷
毋忘隨時來個commit,想說養成save的好習慣
所以以下的事情是家常便飯XD

久而久之一個專案內不小心就充滿了凌亂的commits
 
Git會記錄我們coding的一舉一動
我們已經知道藉由使用branch可以方便的讓我們切換版本
但是魔鬼藏在細節裡
真正紀錄我們細節的,其實就是那些年,無時無刻盯著我們
吃飯、睡覺、打code的commits

當我們每次進行commit的時候,都會有相對應的SHA碼
(SHA是一種演算法,在這邊就當成它是經由複雜的運算,來達到讓每次進行commit的時候,都不會有重複的狀況而採取的一種紀錄方法)
就好像我們每個人都有自己的身分證字號一樣
你可以在這邊找到你的commit他們的身分證字號

Github(網頁版)


Github(桌面版)


很棒吧,他們都已經排排站了喔
紅色框框的地方是你點一下就可以作一個複製的動作。
那今天就是要來示範如何將多餘的commit進行一個整理的動作
讓本來凌亂的紀錄,連小明都看得懂。

首先呢
請移駕到你的command line,我用的是Github提供的那個工具
先cd到你專案的資料夾
git rebase -i [SHA/commit版本號碼]
我這邊直接開最舊的起來改,中途會彈出一個文字檔,你的看起來應該也和下面的差不多:
pick e466349 Loading files
pick 8af9b37 Create README.md
pick 161534d small revision
pick d992172 small revision
pick 2cf85cd easy connected
pick 4769884 Nov 20
pick 92b4484 Nov 21
pick 30d9919 Nov 21 noon
pick bc92c4a Nov evening
pick 4771349 Nov 22
pick 266f43b Nov 23
pick a1990a4 Nov 24
pick 2100e46 Nov 24 v2
pick 196b0dc Nov 25

...

# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
在這邊你可以自己一個一個commit慢慢玩
你會發現最舊commit它保管了最多的pick
換句話說,越舊的commit提供你更大的調整範圍
你可以根據自己的需求來進行pick reowrd edit squash fixup exec等動作
這些動作我想以後再介紹,今天重點就在使用squash

pick和squash最主要的不同是
當你用rebase指令開啟了一個commit
你會看到很多個pick(如果它不是最新的commit)
而當你把其中幾個pick註解掉或是刪掉的話
沒錯,它們就會像變了心的女朋友一樣
頭也不回的走了,永遠都回不來了!

那如果你是用squash的話,就很像是吾皇擁有後宮三千佳麗
卻無奈於經濟不景氣,本來一張鈔票可以買一碗牛肉麵
現在只能買一碗滷肉飯......所以把三千砍到剩千五的道理一樣
squash是向前融合(meld into),所以它的前一個動作不可為squash
(拜託不要呀~千五已經很少了......)

以我自己的專案為例,我只挑了最重要的幾條沒有squash
剩下的都向前融合(這邊用#會讓Git跳過不讀,等於註解):
#一開始的檔案
pick e466349 Loading files
squash 8af9b37 Create README.md
squash 161534d small revision
squash d992172 small revision
squash 2cf85cd easy connected
#建立主機伺服器連線
pick 4769884 Nov 20
squash 92b4484 Nov 21
squash 30d9919 Nov 21 noon
#動畫完成
pick bc92c4a Nov evening
squash 4771349 Nov 22
squash 266f43b Nov 23
squash a1990a4 Nov 24
squash 2100e46 Nov 24 v2
squash 196b0dc Nov 25
squash 2e6cbcd Nov 25 v2
squash bebe100 Nov 25 v3
...
接著請儲存後關掉記事本
沒有意外的話等一會又會有記事本彈出來和你報告:
# This is a combination of 5 commits.
# The first commit's message is:

Loading files

# This is the 2nd commit message:

Create README.md

# This is the 3rd commit message:

small revision

# This is the 4th commit message:

small revision

shake new activity

# This is the 5th commit message:

easy connected

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# rebase in progress; onto 4f12ce8
# You are currently editing a commit while rebasing branch 'ManualEntry-v2' on '4f12ce8'.
#
意思就是
「稟告皇上,在下已經把舊的幾個宮女fire掉了
然後請了一個最新的代替那些位置,您要為她取一個新的名字嗎?」
其實你可以把他們全部砍掉,留一個名稱就好。
「朕,覺der可以叫做Basic Settings。」
「喳!」
Basic settings

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# rebase in progress; onto 4f12ce8
# You are currently editing a commit while rebasing branch 'ManualEntry-v2' on '4f12ce8'.
#

一樣儲存後關掉記事本
中途你可能會遇到重複的現象,一直叫你取名幹嘛的
不過照著範例走,應該不會有問題。

Finally,我們就完成了整理commits的動作。
來ㄧ張selfie~


References


Reflection on using Git

這篇將依照目前使用Git的心得,簡單的對Git與提供保存Git與原始碼在雲端的網路服務Github進行介紹。

Git是什麼? 為什麼要用Git?
Git是一種版本控制的系統。
換句話說,Git可以提供你整個專案版本的監控
藉由使用版本控制系統,你可以自由地在每個版本之間切換
你可以測試不同的版本並且備份你的版本。

Git的備份功能和傳統我們認知的備份有很大的不同。
依照傳統的作法,我們在進行一個專案時,若遇到不同的想法想測試,
我們可能會先將檔案(或整個專案)複製一份,並且用複製出來的新版本進行修改
原版就先進行保存。所以這時候你可能會遇到這種情況:



之後當我們的每個版本都有一定程度的發展,你可能會遇到這種情況:



這種情況我認為是各有優缺啦:
Pros
Cons
  1. 無腦,直覺,不須傷腦筋。在當下可以快速的分離出各種版本,並確定未來的發展。

  1. 檔案一多管理起來將會很傷腦筋,並且很難依照命名來推測出每個版本之間細微的差異。 
  2. 重複的檔案佔額外的容量。

依照傳統的方式,若本來要發展的版本分岐不大,確實可以使用這種方法,因為若沒有進入後續維護的階段,其實非常的省時省力。但一旦踏入了維護的階段,看到那排山倒海而來的檔案......你可能會全身無力......

那使用Git呢? 由於Git是一個工具,我們在學習一樣工具如何使用的過程中多多少少會耗費一些學習的時間,但就我用到目前為止而言,覺得經濟效益確實比傳統的作法好上太多。使用Git需要使用命令工具列(也就是按壓鍵盤Windows+R後,輸入cmd會出現的東西)輸入一些命令,但有了Github提供的圖形化介面之後,其實已經好用很多。
使用Git的優缺點:
Pros
Cons
  1. 自由自在的切換版本(註1)。(尤其在開發Android常常會有同時會牽涉到程式碼與外觀的部分,這時候可以很快的切換版本並執行)
  2. 自由自在的查看版本內每個紀錄的細節(註2)。(像是這次又加了什麼新功能,把什麼東西改掉了)
  3. 備份的方式很像是幫你建立一個還原點,而沒有複製重複的檔案。
  1. Git的使用方式需要學習,且Github與Github提供的圖形化介面軟體目前沒有中文版本。

以前我用傳統的備份方式,有時候常常在不同版本的專案資料夾內把檔案拉來拉去,後來也忘記這些名字一樣的檔案到底哪個是有改過哪個是沒有改過的;或是說有時候不小心在系統詢問要不要取代時不小心按了取代,然後心血便付諸流水。

至少現在我用了Git,我能很肯定地說我可以很大膽的coding,我可以肆無忌憚的對程式碼大興土木,只要在每個關鍵點記得做一個記錄的動作,之後就算我改壞了,一行指令就回來啦。這時候你真的可以體會什麼叫作天無絕人之路、什麼叫作浪子回頭金不換XD

學習使用Git可以參考:
  1. 歐萊禮出版的版本控制使用Git(第二版),內文平易近人。
  2. 繁體中文的官方Git使用手冊,圖文並茂好消化。
註1:Git的checkout
註2:Git的commit

延伸閱讀:我的Git常用指令(準備中)

Change the title of ActionBar

如何改變ActionBar上顯示的標題呢? 如
123

在OnCreate()中加入
setTitle("安安你好嗎");

Solve the json encoding problem of Chinese characters in Java

今天想說要把資料從資料庫中擷取出來顯示在Android的使用者介面上
但因為我是使用php對MySQL做查詢
再將查詢的結果用陣列encode成json格式,再傳回Android
然後我有一個習慣就是我會先在Eclipse的console面板的log中先看傳回什麼東西XD
例如
Log.e("result", json)
於是就看到我的log跑出一堆很奇怪的訊息,類似
\u6211\u662f\u5e25\u54e5
就像這樣,每個都有\u開頭
後來花了一點時間查了一下資料,了解了這原來就是所謂的萬國碼(Unicode)
然後其實也不用花費時間在轉碼上焦頭爛額......因為
就算log顯示的是那樣,Java在輸出(print、TextView)的時候還是會自動轉碼回去
log的時候會產生這樣的結果,是因為json在encode的過程中會將中文自動轉碼
保險一點的話還是可以在語法中加入JSON_UNESCAPED_UNICODE
json_encode($string, JSON_UNESCAPED_UNICODE);
這是php版本5.4以後才有的功能
如此一來json在encode的過程中就不會強迫轉碼囉

References


The Haversine formula

在我們設計App時,有時候我們會需要獲得使用者的地理位置資訊
然後用使用者目前的經緯度座標,計算離使用者地點最近的幾個地點
這時候如果你的資料庫中有存放一些地點的座標資料
那我們就可以使用所謂的Haversine公式,來算出地球上使用者與這些地點座標之間的距離


(簡單的解釋一下每個符號的意義:d是距離,r是弧度,φ是經度,λ是緯度)
然後假設我們資料庫中有一個資料表(table)
且資料表中的每個欄位記錄了每個地點的名稱(name)與座標(lat, lon),例如
table
name
lat
lon
name_1
latitude_1
longitude_1
name_2
latitude_2
longitude_2
...
...
...

接著我們可以將上面的公式轉換一下並且假設我們現在的座標在(23, 120)
在資料庫中就可以用這個語法來查詢
SELECT
     name,
     ( 6371 * acos( cos( radians( 23 ) ) * cos( radians( lat ) ) * cos( radians( lon ) 
     - radians( 120 ) ) + sin( radians( 23 ) ) * sin( radians( lat ) ) ) ) AS distance
FROM
     table
ORDER BY
     distance   
結果就會顯示這些地點的名稱與使用者之間的距離(公里),並且已經依照遠近排序
其中用到的acos()、cos()、sin()、radians()等函數,在MySQL中都可以直接使用

Reference


Haversine formula - Wikipedia, the free encyclopedia
Creating a Store Locator with PHP, MySQL & Google Maps - Google Maps API — Google Developers

Public variables and methods

最近上數位學習的課,正在討論SCORM(Sharable Content Object Reference Model)
SCORM真的是個很神奇的傢伙!
它可以壓縮成package,接著上傳Moodle
然後學習者的一舉一動都會被監視XD
(例如:學習後進行測驗的分數、已經進行學習與否還有學習所耗費的時間等等)

其中談到SCORM的架構時,有說到SCORM使用了標準(standard)
來決定要儲存學習者的哪些資訊
也就是追蹤學習者學習的最小單位(SCOs)所用到的技術

在這過程中不小心就聯想到public變數與method的使用XD
所以就來複習一下吧 :P  這次要探討的是

為什麼在修改資料時,與其直接操作public變數,另外建立method反而是更好的做法?

以下我將用箱子還有球來示範,應該是夠淺顯易懂吧~

使用public變數

public class Box(){
     //假設我們找來了一個箱子(它是一個物件)
     //並且在裡面放進了5顆球,想說等一下要分別發送給5個人
     int ball = 5;
}
然後...有人看到了這個箱子
Box box = new Box();
box.ball = 0;                     //噢不...球球被拿光光了T_T

然後看看這個方式

public class Box(){
     //一樣放進5顆球
     private int ball = 5;
     public Box()

     //規定一次只能放一顆
     public void getOnlyOneBall(){
          ball = ball - 1;
     }
}
之後有人要拿
Box box = new Box();
box.getOnlyOneBall();  //一次只能拿一顆吧!你看看你~
Yes! 這次狀況就好很多了。
所以說呢可以使用一些特別的方法(method),避免有心人士一次就把球球拿光光
或是在不小心的狀況之下本來要放4顆,結果放進了10顆(意外對變數造成修改)

在大多數情況下,我們都希望能夠自由地對變數進行操作。但是誠如Clean Code無瑕的程式碼一書所說:
「將實現的過程隱藏實際上便是一種抽象化的過程。而在這當中,類別不單單只是透過讀取及設定函式讓變數供人存取而已;To be more specific, 它提供了一個抽象介面,讓使用者在不需要知道實現過程的狀態下,還能操縱資料的本質。」
改變世界的九大演算法一書中也有提到:
「撰寫程式碼的原則是盡量隱藏資料。」

Fragment切換,另一種方法

在之前的文章「Fragment切換起手式」中我已經介紹了最基本的方法,就是透過Framelayout作為container
然後使用FragmentTransaction的方式來更換Fragment
其實還存在另外一種方法,這邊我將使用Facebook的一個開放原始碼project來示範

main.xml
<LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">

    <fragment android:name="com.facebook.scrumptious.SelectionFragment"
              android:id="@+id/selectionFragment"
              android:layout_width="match_parent"
              android:layout_height="match_parent" />

    <fragment android:name="com.facebook.scrumptious.SplashFragment"
              android:id="@+id/splashFragment"
              android:layout_width="match_parent"
              android:layout_height="match_parent" />

    <fragment android:name="com.facebook.widget.UserSettingsFragment"
              android:id="@+id/userSettingsFragment"
              android:layout_width="match_parent"
              android:layout_height="match_parent" />

</LinearLayout>
一開始先將所有Fragment的layout都放置在主要Activity的layout中
接著透過hide與show的方式控制Fragment出現的時機

MainActivity.class
        FragmentManager fm = getSupportFragmentManager();
        SplashFragment splashFragment = (SplashFragment) fm.findFragmentById(R.id.splashFragment);

        // fragments[畫面名稱]是個用來存放相關畫面的Class類別陣列
        // 如SPLASH、SELECTION、SETTINGS分別代表登入頁、主選單與設置頁
        fragments[SPLASH] = splashFragment;
        fragments[SELECTION] = fm.findFragmentById(R.id.selectionFragment);
        fragments[SETTINGS] = fm.findFragmentById(R.id.userSettingsFragment);

        FragmentTransaction transaction = fm.beginTransaction();
        for(int i = 0; i < fragments.length; i++) {
            transaction.hide(fragments[i]);

        }
        transaction.commit();
...
private void showFragment(int fragmentIndex, boolean addToBackStack) {
        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction transaction = fm.beginTransaction();

        //將不需要的頁面隱藏,只顯示想展示的頁面
        for (int i = 0; i < fragments.length; i++) {
            if (i == fragmentIndex) {
                transaction.show(fragments[i]);
            } else {
                transaction.hide(fragments[i]);
            }
        }
        if (addToBackStack) {
            transaction.addToBackStack(null);
        }
        transaction.commit();
    }

這種方式也是切換Fragment的一種方式,他同樣不會影響Activity的生命週期
不過有趣的一點是──在Activity執行生命週期的時候
所有的Fragment也會同時執行生命週期XDDDD
也就是說,若你有寫一些code,想要讓使用者載入該Fragment的時候自動執行
使用這個方法的結果就是──它同時載入了所有Fragment的code,並且同時執行
由於Facebook的這個project我認為它每個主要的Fragment其實沒有什麼太特殊的功能
不然的話我想使用這個方法必須經過再三考慮

References


facebook-android-sdk/samples/Scrumptious at master · facebook/facebook-android-sdk

Fragment切換起手式

在Android版本3.0之後發行了Fragment這個新功能
從此之後就不用每次要開新的畫面就得launch新的Activity啦!
這個新功能可以做到同一個Activity內多個畫面的切換
同時又不影響整個Activity的生命週期(同場加映Fragment與Activity互相對應的生命週期)
以下將展示Fragment切換起手式

首先你會準備幾個檔案:
  1. 執行以下代碼的Activity(這邊以MainActivity.class為例) 
  2. 欲更換的Fragment(這邊以FragmentNearby.class為例) 
  3. 整個畫面的layout(這邊以main.xml為例,在欲更換Fragment的地方放置Framelayout元件,且在本例中該元件id為content)
Fragment切換起手式
// new一個新的Fragment類別物件
FragmentNearby fragmentNearby = new FragmentNearby();

// getFragmentManager, 這邊getSupportFragmentManager是為了向較舊的版本相容
FragmentManager fragmentManager = getSupportFragmentManager();

// 進行transaction
FragmentTransaction transaction = fragmentManager.beginTransaction();

// 對Fragment動手動腳,指令額外包括add, remove, hide, show...etc
transaction.replace(R.id.content, fragmentNearby);

// 確認並執行transaction!
transaction.commit();



References


Fragments | Android Developers

Sending arguments via Android, php, and external java



這是一個AMP架構下(Apache, MySQL, php)與Android連接
經由Android傳遞數值經過php再到伺服器端執行java程式後再返回Android的架構

傳遞數值的關鍵步驟大概如下面所列出:
  1. (Android)以EditText取得使用者輸入的數值,並以HTTP POST方式傳輸給php
  2. (php)取得使用者裝置傳來的數值,將數值傳入Java程式並執行
  3. (Java)執行結束後將結果返回給php
  4. (php)取得結果後以json格式封裝,傳回給使用者裝置
  5. (Android)取得結果,解碼json

1.(Android)以EditText取得使用者輸入的數值,並以HTTP POST方式傳輸給php

取得使用者input
String input = editText.getText.toString
將input封裝到params這個List裡
params.add(new BasicNameValuePair("input", input));
用HTTP POST的方式送出資料
httpPost.setEntity(new UrlEncodedFormEntity(params));

2.(php)取得使用者裝置傳來的數值,將數值傳入Java程式並執行

取得使用者input
$input= $_POST[‘input’];
執行外部程式,並代入參數
exec(“java –jar path/to/file.jar input”, $output);

3.(Java)執行結束後將結果返回給php

執行Java
public static void main(String[] args){
String input = args[0]; (中間愛幹嘛就幹嘛) System.out.print(結果); }

4.(php)取得結果後以json格式封裝,傳回給使用者裝置

取得Java傳回來的結果(它是陣列)
$result = output[0];
以json格式封裝
die(json_encode($result));

5.(Android)取得結果,解碼json

取得InputStream的內容
Is=httpEntity.getContent();
最後就可以使用BufferReader與JSONObject的方法使用結果了!

HttpClient起手式

當我們用瀏覽器(像是IE、Chrome、Firefox)在逛網站時
可以想像我們的電腦(客戶端,Client)要和網站的電腦(伺服端,Server)一直都在進行溝通
進行溝通的方式大概就是client對server發出要求(request),然後server送回應(response)給client
接著client再想辦法去解析response中所夾帶的資料~

那通常送出request有兩種方法,分別為POST和GET
兩者之間有一點點不同,但這不是這次要講的內容......
這次要說的是Android和網頁或網站傳遞資料的過程當中
所會用到的HttpClient與相關的常用程式碼寫法:

HttpClient起手式

//new一個新的HttpClient物件
DefaultHttpClient httpClient = new DefaultHttpClient();

//看是要用POST或GET方法,new一個出來,並且指定網址(url)
HttpPost httpPost = new HttpPost(url);

//將要傳遞的參數放到params裡面並設定HttpPost的Entity
httpPost.setEntity(new UrlEncodedFormEntity(params));

//new一個HttpResponse來裝httpClient執行之後的結果
HttpResponse httpResponse = httpClient.execute(httpPost);

//使用HttpEntity把內容拿出來
HttpEntity httpEntity = httpResponse.getEntity();

//(範例)內容可以應用在InputStream中
is = httpEntity.getContent();

關於params(傳遞參數,parameters)的設定,它必須使用NameValuePair格式儲存
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("username", username));
params.add(new BasicNameValuePair("password", password));

延伸問題一、HttpClient好像也很常見,和DefaultHttpClient的差別?

DefaultHttpClient是一個類別(Class),HttpClient是一個介面(Interface)
基本上藉由對HttpClient的實作已經可以應付大部分的狀況 (Default implementation of HttpClient pre-configured for most common use scenarios)
DefaultHttpClient之後也不建議使用了(Deprecated),可以使用HttpClientBuilder、CloseableHttpClient等其他方法

延伸問題二、和透過PHP表單輸入的差別?


在PHP中得透過$_POST["name"]的方式來取得表單中的數值,這邊則得要設定params

References


DefaultHttpClient (Apache HttpClient 4.3.6 API)
DefaultHttpClient is deprecated
【Android进阶学习】Http编程之HttpClient

Jena常用指令

資訊來自Jena javadoc,我將比較常用的翻譯一下
如果沒翻譯的可能就是比較不重要的,或是我現在用不到的。

Model

  • add

    Model add(Resource s, Property p, RDFNode o)
    對目前的Model新增一個statement(triple)。
    Parameters:
    s - the subject of the statement to add
    p - the predicate of the statement to add
    o - the object of the statement to add
    Returns:
    this model
  • createProperty

    Property createProperty(String nameSpace, String localName)
    Create a property with a given URI composed from a namespace part and a localname part by concatenating the strings.This method may return an existing property with the correct URI and model, or it may construct a fresh one, as it sees fit.
    Subsequent operations on the returned property may modify this model.
    Parameters:
    nameSpace - Property的命名空間。
    localName - 你幫該property取的名字。
    Returns:
    a property instance

Resouce

  • addProperty

    Resource addProperty(Property p, String o)
    對Resource(subject or object)建立property與object,成立一個三元關係(triple)。
    Parameters:
    p - 欲新增之property。
    o - property(predicate)所連接的object。
    Returns:
    This resource to allow cascading calls.
  • getProperty

    Statement getProperty(Property p)
    回傳和目前這個resource相關的property與object,若property p是property q的子類別,也會被return。
    Parameters:
    p - 欲查詢之property。
    Returns:
    回傳和目前這個resource相關的property與object,若property p是property q的子類別,也會被return。沒有statement的話會回傳null。

Jena SPARQL

SPARQL是用來查詢RDF graph的一個語法。
RDF graph就是一大堆triple的組成
在Jena中所提到的model,也就是RDF graph,statement就是triples
RDF dataset中則包含了更多的graph

SPARQL設定方面

command Line的部分 使用cd C:\位址 跳槽
若發生JENA_HOME not set
就是環境變數沒有設定
1.先echo %PATH%,看看目前有沒有紀錄到
2.沒有的話入PATH=C:\Program Files\Java\(JDK資料夾)\bin;%PATH%
3.完成!!!!!!!!!!!!!

no query string or query file
bat\sparql.bat --data=vc-db-1.rdf --query=q1.rq
把兩個檔案放到同目錄就好了

基本用法

SELECT ?欄位
WHERE
  {
      條件(必須是triple)
  }

String Matching

{FILTER regex(?g, "r", "i") }  "r"是你想查詢的字串

Testing Values

SELECT ?resource
WHERE
  {
    ?resource info:age ?age .
    FILTER (?age >= 24)          使用FILTER(條件)
  }

OPTIONAL(IF)

SELECT ?name ?age
WHERE
{
    ?person vcard:FN  ?name .
    OPTIONAL { ?person info:age ?age }
}
!bound(?age) 這是一個unbound的動作,意即不符合結果的資料也會列出

在prefix前面有小老鼠的可能是n3格式,把rdf改成n3就可以解碼,如
@prefix foaf: <http://xmlns.com/foaf/0.1/> .

UNION

PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX vCard: <http://www.w3.org/2001/vcard-rdf/3.0#>

SELECT ?name
WHERE
{
   { [] foaf:name ?name } UNION { [] vCard:FN ?name }
}
其實sparql的語法很free,也可以修改成這樣
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX vCard: <http://www.w3.org/2001/vcard-rdf/3.0#>

SELECT ?name
WHERE
{
  [] ?p ?name
  FILTER ( ?p = foaf:name || ?p = vCard:FN )
}
如果把第一個例子的?name改成?name1與?name2,那結果可以更明顯的顯示
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX vCard: <http://www.w3.org/2001/vcard-rdf/3.0#>

SELECT ?name1 ?name2
WHERE
{
   { [] foaf:name ?name1 } UNION { [] vCard:FN ?name2 }
}

---------------------------------
| name1         | name2         |
=================================
| "Matt Jones"  |               |
| "Sarah Jones" |               |
|               | "Becky Smith" |
|               | "John Smith"  |
---------------------------------

Datasets

輸入sparql --help查看全部指令,可以看到 
Dataset
     --data=FILE            Data for the datset - triple or quad formats
     --graph=FILE           Graph for default graph of the datset(指定dataset中的預設graph)
     --namedGraph=FILE      Add a graph into the dataset as a named graph(對graph命名,並且加入dataset)
列出所有資料,語法sparql --graph ds-dft.ttl --namedgraph ds-ng-1.ttl --named graph ds-ng-2.ttl --query xxxxx.rq
PREFIX  xsd:    <http://www.w3.org/2001/XMLSchema#>
PREFIX  dc:     <http://purl.org/dc/elements/1.1/>
PREFIX  :       <.>

SELECT *
{
    { ?s ?p ?o } UNION { GRAPH ?g { ?s ?p ?o } }
}
另外用法,注意GRAPH後面一定要先留空格
PREFIX  xsd:    <http://www.w3.org/2001/XMLSchema#>
PREFIX  dc:     <http://purl.org/dc/elements/1.1/>
PREFIX  :       <.>

SELECT *
{
    { ?s ?p ?o } UNION { GRAPH :ds-ng-1.ttl { ?s ?p ?o } } 列出和ds-ng-1.ttl聯集資料
}
也可以把指令改寫成--query xxxxx.rq 只是要把FROM與FROM NAMED加入取代--graph與--namedgraph
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX dc:  <http://purl.org/dc/elements/1.1/>
PREFIX :    <.>

SELECT *
FROM       <ds-dft.ttl>
FROM NAMED <ds-ng-1.ttl>
FROM NAMED <ds-ng-2.ttl>
{
   { ?s ?p ?o } UNION { GRAPH ?g { ?s ?p ?o } }
}
也可以combo FILTER來使用
PREFIX  xsd:    <http://www.w3.org/2001/XMLSchema#>
PREFIX  dc:     <http://purl.org/dc/elements/1.1/>
PREFIX  :       <.>

SELECT ?date ?title
{
  ?g dc:date ?date . FILTER (?date > "2005-08-01T00:00:00Z"^^xsd:dateTime )
  GRAPH ?g
      { ?b dc:title ?title }
}

Manipulating SPARQL using ARQ

ARQ使用JAVA的方式來操作SPARQL(施工中)

References:


SPARQL Tutorial

Tutorial - Manipulating SPARQL using ARQ

Jena RDF Core API

Jena是使用Java編寫的API,可以用來做本體論的新增、查詢、修改與推論。

若還沒有建立Jena環境,可以參考如何在Eclipse中建立第一個Jena專案

以下在Jena中所提到的model、statement分別可以對應到RDF graph與triple。triple(statement)通常以名詞──動詞──名詞(主詞subject、謂詞predicate、受詞object)的方式表示,例如

Shakespear write Camelot

graph則是一大堆triple的集合。

簡單操作

1.建立Model
從ModelFactory中創造預設的Model
Model model = ModelFactory.createDefaultModel();

2.從Model中建立Resource
Resource johnSmith = model.createResource(personURI);

3.對resource增加property
//addProperty(Property p, RDFNode o)
johnSmith.addProperty(VCARD.FN, fullName);



把所有的triple列出來

1.使用Statement Iterator
StmtIterator iter = model.listStatements();

2.在while迴圈中建立Statement
Statement stmt      = iter.nextStatement(); 

3.抓出getter
Resource  subject   = stmt.getSubject();     // get the subject
Property  predicate = stmt.getPredicate();   // get the predicate
RDFNode   object    = stmt.getObject();      // get the object

4.也可以直接輸出XML格式
model.write(System.out);

Triple-annotation格式
model.write(System.out, "N-TRIPLES");

讀取rdf檔案

1.建立Model
Model model = ModelFactory.createDefaultModel();

2.使用FileManager打開檔案
 InputStream in = FileManager.get().open( inputFileName );

3.列印XML
model.read(in, null);
model.write(System.out);

P.S.:找不到vc-db-1.rdf? 放在和你放jena資料夾一樣的地方就好囉

設定前置命名空間

1.建立Model
Model model = ModelFactory.createDefaultModel();

2.設定前置
 String nsA = "http://somewhere/else#";
 String nsB = "http://nowhere/else#";

3.產生resources, properties,
 Resource root = m.createResource( nsA + "root" );//類似○,代表subject與object 
 Property P = m.createProperty( nsA + "P" );      //類似→,代表predicate
 Resource x = m.createResource( nsA + "x" );      //類似○,代表subject與object

4.將resource與property間的關係建立起來
 m.add( root, P, x );

5.查看結果
 m.write( System.out );

PS.:有沒有m.setNsPrefix的差異如下:
<j.1:P rdf:resource="http://somewhere/else#x"/> //no set
<nsA:P rdf:resource="http://somewhere/else#x"/> //set

m.read("location")
http://stackoverflow.com/questions/15782656/reading-local-owl-file-using-jena-response-filenotfoundexception

抽取URI

1.新增一個Resource, 存放等一下要retrive的resource
Resource vcard = model.getResource("http://somewhere/JohnSmith/");;

2.對Resource使用getProperty,把資料抽出來
String fullName = vcard.getProperty(VCARD.FN).getString();

PS.:這邊getString會直接輸出資料值,使用toString會輸出一整串XD

3.可以對取出的Resource增加資料
vcard.addProperty(VCARD.NICKNAME, "Smithy")

4.使用StmtIterator 來把所有的property列出來
StmtIterator iter = vcard.listProperties(VCARD.NICKNAME);

查詢Model

這邊是說如果什麼事情都用model.listStatement()來做也就太累了
1.所以使用ResIterator來代替Resource的查詢,找出有VCARD:FN屬性的資源
ResIterator iter = model.listSubjectsWithProperty(VCARD.FN);

2.進入if 迴圈 iter.hasNext print 
if (iter.hasNext()) {
    System.out.println("The database contains vcards for:");
    while (iter.hasNext()) {
        System.out.println("  " + iter.nextResource()
                                      .getProperty(VCARD.FN)
                                      .getString());
    }

有另外的方法,使用new SimpleSelector

1.再seletor中分別填入S, P, O
StmtIterator iter = model.listStatements(new SimpleSelector(null, VCARD.FN, (RDFNode) null)

操作Model

關於union, intersection與difference的操作
Model model = model1.union(model2);

容器(container)

關於容器,有三種,分別是BAG, ALT, SEQ
BAG是沒有規則隨便亂放的
ALT雖然也是沒有規則,但是基本上用來放替代品
SEQ是有規則的


Reference:

An Introduction to RDF and the Jena RDF API