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

twitterのsession処理 -init.ja.***.js

twitterで読み込まれるjsのうちの一つ、init.ja.***.jsの中でsessionがどのように処理されているかを見てみます。

https://abs.twimg.com/k/ja/init.ja.***.js
* Copyright(c) 2012-2014 Roman Shtylman -> CEO at Courseoff, Computer whisperer @ Cruise/GM. Formerly Coinbase, Courseoff, other stuff. SF.
* Copyright(c) 2015 Douglas Christopher Wilson ->Lead Software Engineer at Salesforce Marketing Cloud?

init.ja.***.js line.2
setSession, removeSession, getSessionしてますね。

function n(){
	this.setSessionItem=function(t,e){
		window.sessionStorage&&sessionStorage.setItem(t,e)
	},
	this.removeSessionItem=function(t){
		window.sessionStorage&&sessionStorage.removeItem(t)
	},
	this.getSessionItem=function(t){
		return window.sessionStorage&&sessionStorage.getItem(t)
	},
	this.setSessionObject=function(t,e){
		void 0===e?this.removeSessionItem(t):this.setSessionItem(t,JSON.stringify(e))
	},t
	his.getSessionObject=function(t){var e=this.getSessionItem(t);

tとeは

function(){var t=this.$element.text().trim();

e=t.extend({},t.fn[this.type].defaults,e,this.$element.data()),e.delay&&"number"==typeof e.delay&&(e.delay={
	show:e.delay,hide:100}),e},enter:function(n){var r=t(n.target);

sessionに入れる値は、scriptPathとunixtimeは入れたい。

twitterのsession情報の一部

{"product":"webclient","description":"stats:ajax:perf:partner_id_sync:tellapart","duration_ms":60,"start_time_ms":1523669400806,"metadata":"{\"url\":\"https://t.tellapart.com/twitterinitmatch\",\"browser\":{\"chrome\":true,\"version\":\"65.0.3325.181\",\"webkit\":true,\"name\":\"chrome\"}}","_category_":"perftown"}

やっぱり、ajaxで”https://analytics.twitter.com/”にトラッキングデータ送ってる。twitter、お前もか?

return t._evalUrl=function(e){return t.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,throws:!0})},t._evalUrl}.apply(e,n))&&(t.exports=r)},"0y9Y":function(t,e,i){"use strict";(function(t){function n(){this.defaultAttrs({tweetHashtagLinkSelector:".js-stream-tweet .twitter-hashtag, .permalink-tweet .twitter-hashtag",tweetLinkSelector:".js-stream-tweet ...

しかしなぜ、jsの中で処理せずに、jsonとしてsessionに値を入れているんだろう?

amazon・facebook・googleのsession strageを見てみよう

session storageとは?
>sessionStorage に保存されたデータはページのセッションが終了するときに消去されます。ページのセッションはブラウザを開いている限り、ページの再読み込みや復元を越えて持続します。新しいタブやウィンドウにページを開くと、新しいセッションが開始します。

なるほど、やりたいことに近いです。
続いて、各社のsession storageのkeyとvalueを見ていきましょう。

amazon.co.jpのsession storage:
CSM_previousURL
csmtid
rwolLastPage
rwolLastPageSet

CSM_previousURLとrwoLastPageのvalueはcurrent directoryのようです。
なにこれ?

facebookのsession storage:
sp_pi:{“pageInfo”:{“scriptPath”:”/”,”categoryToken”:”a6bebc6e”,”extraData”:{“imp_id”:”hogehoge”}},”clickPoint”:null,”time”:1523665957490}
うお、jsonだ。。。timeはunix timeっぽいですね。

google.co.jpのsession storage:
eob;;xpli p:m|l:14_[“4″,”2”]
_c;;i p:*|l:9007199254hogehoge_16
eob;;xplq p:m|l:15_[“google analytics 滞在時間”,”セッションとは”]
うおーーー前回の検索ワードがsessionに残ってる!

twitter.com/?lang=ja
ScribeTransport
{“internal_referer”:null,”client_version”:”macaw-swift”,”browser_name”:”chrome”,”duration_ms”:4231,”event_namespace”:{“client”:”web”,”page”:”home”,”section”:”home”,”component”:”full”,”element”:”ttft”,”action”:”ttft”},”triggered_on”:1523,”format_version”:2,”_categ..以下略
twitterもjsonでunixtimeが入っています。

あ~、facebook・twitterはunix time使ってるってことは、完全にトラッキングしてますね。amazonもトラッキングしてますね。googleに至ってはセッションに検索ワードが入ってる。なんかもー力抜けてきた。。
各社の設計から考えると、cookieとsessionの性質上、cookieはnewからreturnか判別、ページ遷移の挙動はsessionにunix time入れて取った方が良さそうですね。
要素が多いtwitterが一番参考になりそうです。

yahoo.co.jp
-> session storage empty
rakuten.co.jp
->phoenix, phoenix__rg, RJSCountDownTimer, RALRTID

ページ滞在時間とnew・return userを判定する

cookieのunix timeでページ滞在時間を計算します。

var unix = Math.floor((new Date()).getTime()/1E3);
var visit = "first visit!";
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+";";
		document.write(visit);
	} else {
		visit = "returned user";
		var cEnd = all.indexOf(";",cStart);
		var cnt = all.substring(cStart,cEnd - 10); //_na=NA1.0000000000.
		document.cookie=cnt+unix+";";
		document.write("前回の滞在時間" + (unix - all.substring(cStart+19,cEnd)) + "秒<br>"); 
		document.write(visit);
	}
}else{
	document.write("cookieが使用できません。");
}

cookieのexpireとunix timeの差をどう考えるかですね。
exireをgoogleのように2年として

1.前回のcookie発行から10分以上差がある
unix – all.substring(cStart+19,cEnd) > 60 * 10
-> return userとみなす
2.前回のcookie発行から10分以内
unix – all.substring(cStart+19,cEnd) <= 60 * 10 -> ユーザのページ遷移とみなす
3.新規のcookie 発行
-> new visitor

何分滞在するかはページの性質によって変わってくるので、例えば1時間の動画のページでは、上記のロジックは機能しませんね。
するとセッションでしょうか。。
あれ、よくみたら、indexedDBに
https://googleads.doubleclick.netが入っていますね。

LocalStrageにもgoogleの値が入っています。
google_experiment_mod 602

これはadsenceのようです。

google_experiment_mod is used by Google AdSense for experimenting with which advertisement perform best on a website.

なんだこれは?
google_pub_config {“sraConfigs”:{“2”:{“sraTimeout”:60000},”4″:{“sraTimeout”:60000}}}

よーわからんぞ。

初めての訪問時はcookieにid・unixtimeを発行し2回目以降はunixtimeのみ更新

var cook;
var cStart,cEnd;
var cnt;

var unix = Math.floor((new Date()).getTime()/1E3);

if(navigator.cookieEnabled)
{
	cook=document.cookie + ";";
	console.log(cook);

	cStart = cook.indexOf("_na=",0);
	if(cStart == -1){   
		document.write("1回目の訪問です!");

		var char = "abcdefghijklmnopqrstuvwxyz123456789";
		var id = "";
		for(var i=0; i<10; i++){
			id += char[Math.floor(Math.random()*35)];
		}
		document.cookie="_na=NA."+id+"."+unix+";";
	} else {
		cEnd = cook.indexOf(";",cStart);
		cnt = cook.substring(cStart,cEnd - 10); // cStart + "_na="
		try {
			document.write(cnt);
			document.cookie=cnt+unix+";";
		} catch(e){
			document.write("訪問回数の取得に失敗しました。");
		}
	}
}else{
	document.write("cookieが使用できません。");
}

key:_na
value:NA.5ub8j1xn2p.1523632591

あれ、ブラウザを更新しても、GAのunixtimeが更新されない。。。何故だ????

javascriptでunixtimeを取得

ga.jsだと、new Date()は9か所で書かれてます。大体、new Date()と.getTime()が続けて書かれています。

+(new Date((new Date).getTime()+f)).toGMTString()+"; ")

a=Math.min(a,1E3*Be+a-(new Date).getTime());

this.set(ab,Math.round((new Date).getTime()/1E3));
this.set(y,Math.round((new Date).getTime()/1E3));
if("event"===a.get(sc)){var b=(new Date).getTime(),c=a.b(dc,0),d=a.b(Zb,0);

1E3 とは、10^3 つまり1000のようです。

ga.jsの真似をして1E3で書いてみます。

var date = Math.floor((new Date()).getTime()/1E3);
document.write(date);

おお、少しプログラミングが上達した錯覚を起こしますね。んな訳ない、桑原桑原。