phpからmongodbにinsert

$mng = new MongoDB\Driver\Manager("mongodb://localhost:27017");

$bulk = new MongoDB\Driver\BulkWrite;
$bulk->insert(['visit' => 'new user', 'referrer'=>'/']);
$mng->executeBulkWrite('app.na007', $bulk);

echo "finished";

入りました。

phpのデータが配列の場合、そのままinsertできます。

$mng = new MongoDB\Driver\Manager("mongodb://localhost:27017");

$array = array('visit' => 'return user', 'referrer'=>'/index.php');
$bulk = new MongoDB\Driver\BulkWrite;
$bulk->insert($array);
$mng->executeBulkWrite('app.na007', $bulk);

echo "finished";

素晴らしい!いいですね♪

ちなみに、既にあるcollectionと同じcollectionをつくろうとすると、
以下のように、already existsとなります。
> db.createCollection(‘na007’);
{ “ok” : 0, “errmsg” : “collection already exists”, “code” : 48 }

新しく008のcollectionにdocumentを入れたい場合
->コードに変更なし

$mng = new MongoDB\Driver\Manager("mongodb://localhost:27017");

$array = array('visit' => 'return user', 'referrer'=>'/index.php');
$bulk = new MongoDB\Driver\BulkWrite;
$bulk->insert($array);
$mng->executeBulkWrite('app.na008', $bulk);

echo "finished";

あ、nosqlなので、mysqlのようにifexistでtableを取得して、なければtableをcreate なんて必要はないんでした。完全に忘れていました。なんか、rubyを始めて触った時と同じような感覚になります。

phpでmongodbからデータをfind();

昨日つくったDB名:app、collection:na007をphpで取得します。

$mng = new MongoDB\Driver\Manager("mongodb://localhost:27017");

$filter = [];
$options = [
  'projection' => ['_id' => 0],
  'sort' => ['_id' => -1],
];
$query = new MongoDB\Driver\Query($filter, $options);
$cursor = $mng->executeQuery('app.na007', $query);

foreach ($cursor as $document) {
  var_dump($document);
}

Nice!
stdClassのobjectで取れるんですね。

mysqlのselectに慣れてしまったせいか、恐る恐る触ってます。
あ、twitterのsessionがjsonなのは、twitter analyticsにno sql使ってるからでしょうか?

/lib64/libc.so.6: version `GLIBC_2.14′ not found

php.iniにextension=mongo.soを通しても、
/lib64/libc.so.6: version `GLIBC_2.14′ not found

glibcのversion確認

# ldd --version
ldd (GNU libc) 2.23

2.14より新しいやんけ。。

もう嫌だ。
mongodbは諦めて、google big queryを物色し始める。
https://cloud.google.com/bigquery/?hl=ja
google big queryの料金
https://cloud.google.com/bigquery/pricing?hl=ja
>BigQuery の料金は、ストレージの定額料金とクエリの使用料金に基づいています。
>毎月 10 GB まで無料。
え、10GB? iphone8の最も低いスペックで64GBですぞ。。。

ということで、再度mongoDB phpドライバのインストールに挑戦
そもそもpearってなんだ、ということで、
pearをアップデートしてみる。

# yum update --enablerepo=remi-php56 php-pear

再度,peclをコマンドラインで打つ

pecl install mongo

Build process completed successfully
Installing ‘/usr/lib64/php/modules/mongodb.so’
install ok: channel://pecl.php.net/mongodb-1.4.2
configuration option “php_ini” is not set to php.ini location
You should add “extension=mongodb.so” to php.ini

あ~まじかよ、朝から6~7時間位、これしかやってないんですけど。

viでphp.iniに”extension=mongodb.so” を追加して、httpd restart

phpinfo()

php-pearを最新にしてpecl install mongoしないと駄目なのね。
どっか旅に出たい。

mongoDB PHPドライバのインストール

先ほど作ったcollectionにアクセスしたいと思います。

$m = new Mongo();
$db = $m0>selectDB("app");
$col = $db->selectCollection("na007");
$doc = $col->find();
var_dump($doc);

おっと
192.168.33.1:49742 [500]: /test4.php – Class ‘Mongo’ not found in /home/vagrant/cookie/test4.php on line 2

pearをインストール

yum install php-pear
pear upgrade --force Archive_Tar
pear upgrade --force Console_Getopt
pear upgrade PEAR

>>XML Extension not found
なに!?

こちらの記事を参考に、php -m とphp -n -mを打つ
https://serverfault.com/questions/589877/pecl-command-produces-long-list-of-errors

$ php -m
bz2
calendar
...
xml

[vagrant@localhost ~]$ php -n -m
Core
date
ereg
filter
hash
libxml
mhash
openssl
pcntl
pcre
readline
Reflection
session
SPL
standard
zlib

vi `which pecl`で、-nを削除します。

#!/bin/sh
exec /usr/bin/php -C -n -q -d include_path=/usr/share/pear \
    -d date.timezone=UTC \
    -d output_buffering=1 /usr/share/pear/peclcmd.php "$@"
pecl install mongo

Download of “pecl/mongo” succeeded, but it is not a valid package archive

なに????
stack overflowを見ます。
https://stackoverflow.com/questions/25590621/pecl-install-mongo-command-for-mongo-1-5-5-tgz-produce-invalid-tgz-error

再度インストール

sudo pecl install --nocompress mongo

引き続き、
Download of “pecl/mongo” succeeded, but it is not a valid package archive

頭来た、php.netを参考に手動でインストール
http://php.net/manual/ja/mongo.installation.php

$ tar zxvf mongo-1.6.16.tar
$ cd mongo-1.6.16
$ phpize
$ ./configure
$ make all
$ sudo make install

mongo.soが/usr/lib64/php/modulesに通ったので、php.iniでextension=mongo.soを追加
httpd restart

error messasge
/lib64/libc.so.6: version `GLIBC_2.14′ not found
はあ???

mongoDB基本操作

// app dbを使う
use app
// collection ‘na007’ を作成
db.createCollection(‘na007′);
// collectionにdocumentをinsert
db.na007.insert({訪問時間:’2018-04-15 07:27:13′, ip:’192.168.33.1’});
// collection ‘na007’からfind
db.na007.find();

nosqlなので、data typeを指定する必要がありません。
先頭のカラムに”_id”: ObjectID(“hogehoge”); というidが自動的に附番されています。これは、ハッシュ化されたような英数字の為、autoincrementではなさそうです。(mongoDBの中のアルゴリズムで、順番をソートできるようにしているかもしれません)

なかなかお洒落ですね。気になるのはdocumentのカラム

// シングルクオテーションで囲ってinsert
db.na007.insert({‘visit’:’Returned User’, ‘referrer’:’/view.php’});

->結果
{ “_id” : ObjectId(“5ad283afb83c546bc5d039bd”), “visit” : “Returned User”, “referrer” : “/view.php” }
囲っても囲わなくても、同じですね。

documentがjsonみたいなので、jsonと相性がいいかもしれませんね。
insert、updateやfind()の機能はいっぱいあるようですが、全通りやっても覚えきれないので、作りながら必要な機能を学んでいきたいと思います。

MongoDB fundamental

mongoDBの構造

- DB
 - Collection
   - Document
   - Document
   - Document
   - Document
   - Document
   - Document

collectionがtableのようなもので、Documentがレコードのようなもの

$ mongoで入る

[vagrant@localhost cookie]$ mongo

use dbnameでdb作成

> show dbs
local  0.078GB
mydb   0.078GB

> use app
switched to db app

mysqlとmongodbの比較

まずmysqlとmongodbの性能比較

RDBMS
システム全体の基本バックエンド
MongoDB
システムの中の数機能(性能とある程度の機能が欲しい場所)のバックエンド

MongoDBとMyISAMのデータ構造や実装は似ている
COUNTのカーソル舐めや、GROUP BYの全データ舐めなどは、性能が似通っている

単純な用途で、かつ、範囲検索がしたい場合はMongoDBはお勧めできる
MongoDBの範囲検索は早い
PRIMARYキーの範囲検索+カウントでは、MyISAMが顕著に早い

MySQL:世界シェアは2位、常にOracleやSQLServerとその座を争ってきた
MongoDB:JSONをそのまま格納したようなデータベース、トランザクション及びリレーション(JOIN句など)機能を持たないが、それと引き替えにスケーラビティと速度を得ている

table一つでjoinを使わない為、mongodb採用としたいと思います。
※vagrantにmongodb 3.2.10がインストールされた状態です。

[vagrant@localhost cookie]$ mongo
MongoDB shell version: 3.2.10
connecting to: test
Server has startup warnings:
2018-04-14T22:22:56.736+0900 I CONTROL  [initandlisten]
2018-04-14T22:22:56.736+0900 I CONTROL  [initandlisten] ** WARNING: soft rlimits too low. rlimits set to 1024 processes, 64000 files. Number of processes should be at least 32000 : 0.5 times number of files.

cookie, session情報をajaxでpostする

viewから呼び込んだjsで、cookie, session情報を配列にpushして、ajaxでサーバー側にpostします。
na.js

var unix = Math.floor((new Date).getTime()/1E3);
var visit = "New User";
if(navigator.cookieEnabled){
    var all=document.cookie + ";";
    var cStart = all.indexOf("_na=",0);
    if(cStart == -1){   
        var char = "abcdefghijklmnopqrstuvwxyz123456789";
        var id = "";
        for(var i=0; i<10; i++){
            id += char[Math.floor(Math.random()*35)];
        }
        var expire = new Date();
        expire.setTime(expire.getTime() + 1000*3600*24*365*2);
        document.cookie="_na=NA1."+id+"."+unix+";expires=" + expire.toUTCString();
        a.push(['vist',visit]);
        a.push(['referrer',document.referrer]);
    } else {
        visit = "Returned User";
        a.push(['visit',visit]);
        var c = window.sessionStorage.getItem(['ScribeTransport']);
        if(c){
            d = JSON.parse(c);
            a.push(['referrer',d.path]);
            a.push(['滞在時間',(unix - d.time)]);        
        }       
    }
}
var data = '{"path":"'+location.pathname+'", "time":"'+unix+'"}';
window.sessionStorage.setItem(['ScribeTransport'],[data]);

a.push(['host',location.host]);
$(function(){
    $(document).ready(function(){
            var postData = {"userdata": a};
            $.post(
                "doubleclick.php",
                 postData,
                 function(data){
                    $(".box").html(data);
                }
            );
        });
});

doubleclick.php
$_POSTを受け取ります。

$date = date("Y-m-d H:i:s");
$data = $_POST['userdata'];
echo "アナリティクスアカウントID:" .$data[0][1]."<br><br>訪問時間:" .$date . "<br> ip:" .$data[1][1]."<br> user:" .$data[2][1]."<br> リファラー:".$data[3][1]."<br> 前のページでの滞在時間:".$data[4][1]."秒<br>";

ちゃんとsession, cookieがsetされ、配列がpostされました。

さあ、次は、postされたデータをどう処理してDBに格納するかですね。
analyticsのview(主にchart.jsを想定)も一緒につくりながらやった方が、効率良さそうな気がします。

sessionとcookieで閲覧開始・リファラー・滞在時間を取得する

1.cookieがsetされていればreturn user, setされていなければnew user
2.new userの場合、document.referrerで、リファラーを取得
3.cookieがsetされていれば、sessionのlocation.pathnameでリファラーを取得
4.cookieがsetされていれば、sessionのunixtimeの差でページ滞在時間を取得

var unix = Math.floor((new Date()).getTime()/1E3);
var data = '{"path":"'+location.pathname+'", "time":"'+unix+'"}';
var visit = "new user<br>";
if(navigator.cookieEnabled)
{
	var all=document.cookie + ";";
	var cStart = all.indexOf("_na=",0);
	if(cStart == -1){   
		var char = "abcdefghijklmnopqrstuvwxyz123456789";
		var id = "";
		for(var i=0; i<10; i++){
			id += char&#91;Math.floor(Math.random()*35)&#93;;
		}
		document.cookie="_na=NA1."+id+"."+unix+";";
		window.sessionStorage.setItem(&#91;'ScribeTransport'&#93;,&#91;data&#93;);
		document.write(visit);
		document.write("リファラー" + document.referrer + "<br>");
	} else {
		visit = "returned user<br>";
		document.write(visit);
		var a = window.sessionStorage.getItem(['ScribeTransport']);
		b = JSON.parse(a);
		var unix = Math.floor((new Date()).getTime()/1E3);
		document.write("リファラー" + b.path + "<br>");
		document.write("滞在時間" +(unix - b.time)+ "秒");
		window.sessionStorage.setItem(['ScribeTransport'],[data]);
	}
}else{
	window.sessionStorage.setItem(['ScribeTransport'],[data]);
}

あれ、待てよ、cookieがsetされていても、sessionがsetされていないパターンもあるな。
if(a)と書き換えます。cookieのexpireは1000*3600*24*365*2(730日)とします。

var unix = Math.floor((new Date).getTime()/1E3);
var visit = "new user<br>";
if(navigator.cookieEnabled){
	var all=document.cookie + ";";
	var cStart = all.indexOf("_na=",0);
	if(cStart == -1){   
		var char = "abcdefghijklmnopqrstuvwxyz123456789";
		var id = "";
		for(var i=0; i<10; i++){
			id += char&#91;Math.floor(Math.random()*35)&#93;;
		}
		var expire = new Date();
		expire.setTime(expire.getTime() + 1000*3600*24*365*2);
		document.cookie="_na=NA1."+id+"."+unix+";expires=" + expire.toUTCString();
		document.write(visit);
		document.write("リファラー" + document.referrer + "<br>");
	} else {
		visit = "returned user<br>";
		document.write(visit);
		var a = window.sessionStorage.getItem(['ScribeTransport']);
		if(a){
			b = JSON.parse(a);
			document.write("リファラー" + b.path + "<br>");
			document.write("滞在時間" +(unix - b.time)+ "秒");			
		}		
	}
}
var data = '{"path":"'+location.pathname+'", "time":"'+unix+'"}';
window.sessionStorage.setItem(['ScribeTransport'],[data]);

次は、このnew user・return user, リファラー, 滞在時間のデータを、サーバー側(ここではdoubleclick.php)に渡すところですね。

javascriptでsessionをget・setする

<script>
	window.sessionStorage.setItem(['ScribeTransport'],['WebData']);
</script>

pathとunix timeを入れます。

<script>
	var unix = Math.floor((new Date()).getTime()/1E3);
	var data = '{"path":"'+location.pathname+'", "time":"'+unix+'"}';
	window.sessionStorage.setItem(['ScribeTransport'],[data]);
</script>

ここまではOK

セットされたsessionを、JSON.parseで取り出す。

<script>
	var a = window.sessionStorage.getItem(['ScribeTransport']);
	b = JSON.parse(a);
	document.write(b.path);
	document.write(b.time);

	var unix = Math.floor((new Date()).getTime()/1E3);
	var data = '{"path":"'+location.pathname+'", "time":"'+unix+'"}';
	window.sessionStorage.setItem(['ScribeTransport'],[data]);
</script>

来た!

sessionから滞在時間とリファラーを抽出する

	var a = window.sessionStorage.getItem(['ScribeTransport']);
	b = JSON.parse(a);
	var unix = Math.floor((new Date()).getTime()/1E3);
	document.write("リファラー" + b.path + "<br>");
	document.write("滞在時間" +(unix - b.time)+ "秒");
	var data = '{"path":"'+location.pathname+'", "time":"'+unix+'"}';
	window.sessionStorage.setItem(['ScribeTransport'],[data]);

OKOKOK