<repo_name>AnkeTarou/Anke<file_sep>/webserver/routes/follow.js
const dbo = require('../lib/mongo');

exports.get = function(req,res){
  res.render("follow",{result:"ssss"});
}
<file_sep>/webserver/views/js/lib.js
var key = {
  index:0,
  topId:"1",
  bottomId:"-1",
  type:"search"
};
var conFlg = false;
var newResultBox = [];
function connect(uri,data,callback,error){
  const req = new XMLHttpRequest();

  //レスポンスが返ってきたときの処理
  req.addEventListener("load",function(e) {
    console.log("connect");
    console.log("uri",uri);
    console.log("送信データ",data);
    console.log("受信データ",req.response);

    callback(req.response);
  })
  req.addEventListener("error",function(e){
    console.error(uri+"でエラーが起きています。");
    console.error("入力値:",data);
    if(error){
      error()
    }
  })
  req.responseType = "json"
  req.open('POST', uri);
  req.setRequestHeader("Content-type", "application/JSON");
  req.send(JSON.stringify(data));
}
function updateResult(response){
  const res = response.result;
  const result = document.getElementById("result");
  for(let index in res){
    const card = createCard(res[index]);

    result.appendChild(card);

    createBody(res,res[index]);
  }
  setActionVote();
  key.index += res.length;
  key.bottomId = res[res.length-1]._id;
  document.getElementById("loader").setAttribute('style','display:none;');
  conFlg = response.conFlg;
  console.log("done");
}

function addNewResult(){
  const result = document.getElementById("result");
  for(let index in newResultBox){
    const child = document.getElementById(key.topId + "-card");
    const card = createCard(newResultBox[index]);

    result.insertBefore(card,child);
    key.topId = newResultBox[index]._id;

    createBody(newResultBox,newResultBox[index]);
  }
  setActionVote();
  newResultBox.length = 0;
  this.setAttribute("style","display: none;");
  document.getElementById("noResulttext").setAttribute("style","display: none;");
}

function createCard(data){
  const card = document.createElement("div");
  card.setAttribute("id",data._id + "-card");
  card.setAttribute("class","card");
  card.setAttribute("style","margin-top:10px;");
  card.setAttribute("key",data._id);

  const header = document.createElement("div");
  header.setAttribute("class","card-header");
  header.setAttribute("role","tab");
  header.setAttribute("style","background-color:white");

  const img = document.createElement("img");
  img.setAttribute("class","rounded-circle");
  img.setAttribute("height","46");
  img.setAttribute("src","/image/" + data.inventory.img);
  img.setAttribute("align","top");
  img.setAttribute("onerror","this.src='/image/default.jpg'")
  header.appendChild(img);

  const label1 = document.createElement("label");
  label1.setAttribute("class","ml-3");
  label1.innerHTML = data.inventory.nickname;
  header.appendChild(label1);

  const label2 = document.createElement("label");
  label2.setAttribute("class","text-center text-muted");
  label2.innerHTML = "@" + data.senderId;
  header.appendChild(label2);

  const a = document.createElement("a");
  a.setAttribute("class","text-body ml-5");
  a.setAttribute("data-toggle","collapse");
  a.setAttribute("href","#" + data._id);
  a.setAttribute("role","button");
  a.setAttribute("aria-expanded","false");
  a.setAttribute("aria-controls","collapseOne");
  a.innerHTML = data.query;
  header.appendChild(a);

  const form = document.createElement("div");
  form.setAttribute("class","form-check-inline");
  header.appendChild(form);

  const i = document.createElement("i");
  i.setAttribute("class","far fa-chart-bar");
  i.innerHTML = data.total;
  form.appendChild(i);

  const label3 = document.createElement("label");
  label3.setAttribute("for",data._id + "-favorite");

  const inp = document.createElement("input");
  inp.setAttribute("id",data._id + "-favorite");
  inp.setAttribute("type","checkbox");
  inp.setAttribute("style","display:none;");
  inp.setAttribute("onclick","favorite(this)");
  if(data.myfavorite){
    inp.setAttribute("checked","true");
  }
  label3.appendChild(inp);

  const i2 = document.createElement("i");
  i2.setAttribute("id",data._id + "-star");
  if(data.myfavorite){
    i2.setAttribute("class","fas fa-star");
    i2.setAttribute("style","color:yellow");
  }else {
    i2.setAttribute("class","far fa-star");
    i2.setAttribute("style","color:#000000");
  }
  i2.textContent = " " + data.favorite;
  label3.appendChild(i2);

  form.appendChild(label3);

  const body = document.createElement("div");
  body.setAttribute("id",data._id);
  body.setAttribute("class","card-body collapse");
  body.setAttribute("role","tabpanel");
  body.setAttribute("aria-labelledby","headingOne");
  body.setAttribute("data-parent","#accordion");

  card.appendChild(header);
  card.appendChild(body);

  return card;
}

function createBody(array,data){
  const contents = document.getElementById(data._id);
  if(data.result){
    const canvas = document.createElement("canvas");
    contents.appendChild(canvas);
    graph(canvas,data)
  }else{
    contents.innerHTML =
    `<table class="table">
      <tbody>`
      +
      function(){
        let answers = "";
        for(let j in data.answers){
          answers +=
          `<tr>
            <td>
              <div class="custom-control custom-` + data.type + `">
                <input id=` + data._id + "-" + j + ` name=` + data._id + "-answer" + ` type=` + data.type + ` class="custom-control-input">
                <label class="custom-control-label" for=` + data._id + "-" + j +`>` + data.answers[j].answer + `</label>
              </div>
            </td>
          </tr>`
        }
        return answers;
      }()
      +
      `</tbody>
    </table>
    <button type="button" class="btn btn-primary" name="vote">投票</button>
    <div class="border-top"></div>`;
  }
}

function setActionVote(){
  const btns = document.getElementsByName("vote");
  for(let btn of btns){
    btn.addEventListener("click",function(e){
      const card = e.target.parentElement;
      const answers = document.getElementsByName(card.id + "-answer");
      const obj = {
        id:card.id,
        index:[]
      }
      for(let answer of answers){
        if(answer.checked){
          obj.index.push(1);
        }else{
          obj.index.push(0);
        }
      }
      connect("/vote/",obj,function(newResultBox){
        card.innerHTML = "";
        const canvas = document.createElement("canvas");
        card.appendChild(canvas);
        graph(canvas,newResultBox);
      })
    })
  }
}

function graph(canvas,array){
  canvas.parentNode.style.height= array.answers.length*60+"px";
  const chartData = function(data){
    const obj = {
      labels:[],
      datasets:[
        {
          label:"なし",
          data:[],
          backgroundColor: function(context) {
              console.log(this)
              var index = context.dataIndex;
              var value = context.dataset.data[index];
              return value < 0 ? 'red' :  // 赤で負値を描画する
                  index % 2 ? 'blue' :    // 負値でなければ、青と緑の交互
                  'green';
          }
        }
      ]
    };
    for(let d of data.answers){
      obj.labels.push(d.answer);
      obj.datasets[0].data.push(d.total);
    }
    return obj;
  }(array)
  new Chart(canvas, {
    // 作成するグラフの種類
    type: 'horizontalBar',
    // ラベルとデータセットを設定
    data: chartData,
    //オプション設定
    options: {
      legend: {
          display: false,            //詳細ラベルの表示
      },
      scales: {                      //軸設定
        yAxes: [{                    //表示設定
          stacked: false,
          //y軸の数字
          ticks: {
            min: 0,                   //最小値
            fontSize: 18,             //フォントサイズ
            stepSize: 10,             //軸間隔
            fontColor: "#00f"
          },
          //y軸グリッド
          gridLines:{           //グリッド設定
            display:true,
            color:"rgba(0,0,0,0.8)"
          }
        }],
        xAxes: [{                         //x軸設定
          stacked: false,
          categoryPercentage: 0.4,      //棒グラフ幅
          scaleLabel: {                 //軸ラベル設定
            display: true,             //表示設定
            fontSize: 18               //フォントサイズ
          },
          //x軸の数字
          ticks: {
            fontSize: 16,             //フォントサイズ
            fontColor:"#fff",
            tickMarkLength:1
          },
          //x軸グリッド
          gridLines:{           //グリッド設定
            display:true,
            color:"rgba(0,0,0,0.3)",
          }
        }],
      },
      animation:{
        duration:1500, //アニメーションにかける時間
        easing:"easeInQuad"
      },
      tooltips:{
        mode:'label' //マウスオーバー時に表示されるtooltip
      }
    }
  });
}
function favorite(btn){
  const req = new XMLHttpRequest();
  const data = {
    targetId:btn.parentElement.parentElement.parentElement.parentElement.getAttribute("key"),
    favorite:btn.checked ? 1 : 0
  }
  //レスポンスが返ってきたときの処理
  req.addEventListener("load",function(e) {
    console.log(req.response)
    if(req.response.status == "success"){
      document.getElementById(data.targetId + "-favorite").parentElement.lastElementChild.textContent = req.response.favorite;
      if(data.favorite === 1){
        document.getElementById(data.targetId + "-favorite").parentElement.lastElementChild.setAttribute("style","color:red;");
      }else {
        document.getElementById(data.targetId + "-favorite").parentElement.lastElementChild.setAttribute("style","color:#000000;");
      }
    }
  })
  req.responseType = "json"
  req.open('POST', 'favorite');
  req.setRequestHeader("Content-type", "application/JSON");
  req.send(JSON.stringify(data));
}
<file_sep>/webserver/app.js
const express = require('express');
const cookieParser = require('cookie-parser')
const app = express();
const bodyParser = require('body-parser');
const indexRouter = require('./routes/index');

// 外部から参照できるファイルを指定
app.use(express.static('views'));
// clientから送られてくるデータを扱いやすい形式に変換
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json({ type: 'application/json' }));
// cookieの設定
app.use(cookieParser());

// アクセスしてきたときに最初に実行される処理
app.use(function(req,res,next){
  console.log("url\n"+req.url);
  console.log("セッション\n"+JSON.stringify(req.session));
  console.log("cookie\n"+JSON.stringify(req.cookies));
  console.log("受信データ\n");
  console.log("POST\n",JSON.stringify(req.body));
  console.log("GET\n",JSON.stringify(req.query));
  next();
});
app.set('view engine', 'ejs');
app.use(indexRouter);

app.listen(80);
<file_sep>/webserver/routes/index.js
var express = require('express');
var router = express.Router();

const dbo = require('../lib/mongo');
const post = require('./post');
const search = require('./search');
const vote = require('./vote');
const favorite = require('./favorite');
const home = require('./home');
const login = require('./login');
const follow = require('./follow');
const insertuser = require('./insertuser');

router.get('/',function(req,res,next){
  console.log("get",req.cookies._id && req.cookies.sessionkey);
  if(req.cookies._id && req.cookies.sessionkey){
    next();
  }else{
    if(req.url == '/login' || req.url == '/search'){
      next();
    }else {
      res.redirect('/login');
    }
  }
});
router.post('/',function(req,res,next){
  console.log("post",req.cookies._id && req.cookies.sessionkey);
  if(req.cookies._id && req.cookies.sessionkey){
    next();
  }else{
    if(req.url == '/login' || req.url == '/search' || req.url == '/home'){
      next();
    }else {
      res.json('err');
    }
  }
})

router.get('/',home.get);
router.post('/home',home.post);

router.get('/search',search.get);
router.post('/search',search.post);

router.post('/post',post.post);

router.post('/vote',vote.post);

router.post('/favorite',favorite.post);

router.get('/follow',follow.get);

router.post('/insertuser',insertuser.post);

router.get('/usercreate',function(req,res){
  res.render('usercreate');
});

router.get('/login',function(req,res){
  if(req.cookies._id && req.cookies.sessionkey){
    res.redirect('/');
  }else {
    res.render('login');
  }
});
router.post('/login',login.post);

module.exports = router;
<file_sep>/webserver/routes/home.js
const dbo = require('../lib/mongo');

exports.get = function(req,res){
  let user = req.cookies;

  const key = [
    {$lookup:{
        from:"question",
        let:{userId:"$_id"},
        pipeline:[{$match:{$expr:{$eq:["$$userId","$senderId"]}}}],
        as:"inventory"
    }},
    {$match:{_id:user._id}},
    {$project:{
      followcount:{$size:"$follow"},
      followercount:{$size:"$follower"},
      favoritecount:{$size:"$favorite"},
      nickname:1,
      img:1,
      postcount:{$size:"$inventory"}
    }}
  ]

  dbo.aggregate("user",key)
  .then(function(result){
    console.log(result);
    res.render("home",result[0]);
  })
  .catch(function(err){
    console.log(err);
    res.render("home");
  })
}

exports.post = function(req,res){
  let user = req.cookies;
  if(!user){
    user = {
      _id:"",
      sessionkey:""
    };
  }
  const userKey = [
    {$match:{_id:user._id}},
    {$project:{
      follow:1
    }}
  ]
  dbo.userCheck(user)
  .then(function(usercheck){
    if(usercheck){
      return true;
    }else{
      return false;
    }
  })
  .then(function(usercheck) {
    if(usercheck){
      // ユーザー認証成功
      //検索して結果を返す
      const index = indexCheck(req.body.index,req.body.type);
      const questionKey = [
        {$lookup:{
          from:"user",
          let:{senderId:"$senderId",follow:"$follow"},
          pipeline:[{$match:{$expr:{$eq:["$$senderId","$_id"]}}}],
          as:"inventory"
        }},
        {$match:{$or:[{"senderId":{$in:["testuser"]}}]}},
        {$unwind:"$answers"},
        {$unwind:"$inventory"},
        {$group:{
          _id:"$_id",
          senderId:{$first:"$senderId"},
          query:{$first:"$query"},
          type:{$first:"$type"},
          answers:{$push:{
            answer:"$answers.answer",
            total:{$size:"$answers.voter"}
          }},
          answer:{$push:{
            answer:"$answers.answer"
          }},
          voters:{$first:"$voters"},
          comment:{$first:{$size:"$comment"}},
          favorite:{$first:"$favorite"},
          date:{$first:"$date"},
          inventory:{$first:"$inventory"}
        }},
        {$project:{
          _id:1,
          senderId:1,
          query:1,
          type:1,
          total:{$size:"$voters"},
          comment:1,
          favorite:{$size:"$favorite"},
          date:1,
          "inventory.nickname":1,
          "inventory.img":1,
          "inventory.follow":1,
          answers:{
            $cond:{
              if:{$in:[user._id,"$voters"]},
              then:"$answers",
              else:"$answer"
            }
          },
          result:{
            $cond:{
              if:{$in:[user._id,"$voters"]},
              then:true,
              else:false
            }
          },
          myfavorite:{
            $cond:{
              if:{$in:[user._id,"$favorite"]},
              then:true,
              else:false
            }
          }
        }},
        {$sort:{date:-1}},
        {$skip:index},
        {$limit:15}
      ];
      dbo.aggregate("question",questionKey)
      .then(function(result1){
        /**** 検索結果を整形する ****/
        let conFlg = (result1.length == 15); //次の該当項目が存在するかどうか
        let size = result1.length;
        for(let i = 0; i < size; i++){
          // どのリクエストからか判別
          if(req.body.type == "new"){
            if(result1[i]._id == req.body.topId){
              result1.splice(i, size-i);
              break;
            }
          }else{
            if(result1[i]._id == req.body.bottomId){
              result1.splice(0, i+1);
              i = 0;
              size = result1.length;
            }
          }
        }
        //　レスポンスオブジェクトの生成
        const response = {
          result:result1,
          conFlg:conFlg
        }
        res.json(response);
      })
      .catch(function(err){
        console.log(err);
        res.json(null)
      })
    }else{
      // ユーザー認証失敗ならnullを返す
      res.json(null);
    }
  })
  .catch(function(error) {
    console.log(error);
    res.json(null);
  });
}

/**
 *indexの形式をチェック
 *引数param
 *@ <int> req.body.index
 *@ <String> req.body.type
 *return <int> 0 or index
**/
function indexCheck(num,type){
  let index;
  if(Number.isNaN(index = parseInt(num)) || type == "new"){
    return 0;
  }
  return index;
}
<file_sep>/webserver/ip.js
module.exports = {
  ip:"192.168.5.5"
  //ip:"127.0.0.1"
}
<file_sep>/webserver/routes/search.js
const dbo = require('../lib/mongo');

// 検索処理　１単語のみの検索に対応
exports.get = function(req,res){
  let obj = {
    sort:sortCheck(req.query.sort),
    order:orderCheck(req.query.order),
    text:textCheck(req.query.text),
    page:"search",
    index:0
  }
  res.render("search",obj);
}

exports.post = function(req,res){
  // 検索キーを生成
  let keyObj = createKeyObj(
    sortCheck(req.body.sort),
    orderCheck(req.body.order),
    textCheck(req.body.text),
    indexCheck(req.body.index,req.body.type),
    pageCheck(req.body.page),
    req.cookies
  );
  console.log(req.cookies);
  //  未ログインなら検索結果だけを返す
  if(!(req.cookies._id && req.cookies.sessionkey)){
    dbo.aggregate("question",keyObj)
    .then(function(result){
      /**** 検索結果を整形する ****/
      let conFlg = (result.length == 15); //次の該当項目が存在するかどうか
      let size = result.length;
      for(let i = 0; i < size; i++){
        // どのリクエストからか判別
        if(req.body.type == "new"){
          if(result[i]._id == req.body.topId){
            result.splice(i, size-i);
            break;
          }
        }else{
          if(result[i]._id == req.body.bottomId){
            result.splice(0, i+1);
            i = 0;
            size = result.length;
          }
        }
      }
      //　レスポンスオブジェクトの生成
      const response = {
        result:result,
        conFlg:conFlg
      }
      res.json(response);
    });
  }
  // ログイン状態かチェック
  if(req.cookies._id && req.cookies.sessionkey){
    dbo.userCheck(req.cookies)
    .then(function(usercheck){
      if(usercheck){
        return true;
      }else{
        return false;
      }
    })
    .then(function(usercheck) {
      if(usercheck){
        // ユーザー認証成功
        //検索して結果を返す
        dbo.aggregate("question",keyObj)
        .then(function(result){
          /**** 検索結果を整形する ****/
          let conFlg = (result.length == 15); //次の該当項目が存在するかどうか
          let size = result.length;
          for(let i = 0; i < size; i++){
            // どのリクエストからか判別
            if(req.body.type == "new"){
              if(result[i]._id == req.body.topId){
                result.splice(i, size-i);
                break;
              }
            }else{
              if(result[i]._id == req.body.bottomId){
                result.splice(0, i+1);
                i = 0;
                size = result.length;
              }
            }
          }
          //　レスポンスオブジェクトの生成
          const response = {
            result:result,
            conFlg:conFlg
          }
          res.json(response);
        })
        .catch(function(error) {
          console.log("question aggregateエラー",error);
        });
      }else{
        // ユーザー認証失敗ならnullを返す
        console.log("user認証失敗",user);
        res.json(null);
      }
    })
    .catch(function(error) {
      console.log("userCheckエラー",error);
    });
  }
};

/**
 *textの形式をチェック
 *引数param
 *@ <String> req.body.text
 *return <String> text
**/
function textCheck(text){
  if(text == undefined){
    text = "";
  }
  return text.toString();
}

/**
 *sortの形式をチェック
 *引数param
 *@ <String> req.body.sort
 *return <String> total or sort
**/
function sortCheck(sort) {
  let check = (sort == "total") || (sort == "good") || (sort == "date");
  if(!check){
    return "date";
  }else{
    return sort;
  }
}

/**
 *orderの形式をチェック
 *引数param
 *@ <int> req.body.order
 *return <int> -1 or order
**/
function orderCheck(order) {
  let check = (order == 1) || (order == -1);
  if(!check){
    return -1;
  }else{
    return parseInt(order);
  }
}

/**
 *indexの形式をチェック
 *引数param
 *@ <int> req.body.index
 *@ <String> req.body.type
 *return <int> 0 or index
**/
function indexCheck(num,type){
  let index;
  if(Number.isNaN(index = parseInt(num)) || type == "new"){
    return 0;
  }
  return index;
}

/**
 *pageの形式をチェック
 *引数param
 *@ <String> req.body.page
 *return <String> "search" or page
**/
function pageCheck(page){
  if(page == "user" || page == "home"){
    return page.toString();
  }else{
    return "search";
  }
}

/**
 *検索キーを生成して返す
 *引数param
 *@ <String> req.body.sort
 *@ <int> req.body.order
 *@ <String> req.body.text
 *@ <int> req.body.index
 *@ <object> req.cookies.user
 *return <object> keyObj
**/
function createKeyObj(sort,order,text,index,page,user) {
  if(!user){
    user = {_id:""};
  }
  const key = {$regex:".*"+text+".*"};
  let keyObj = [];

  keyObj[0] = {$lookup:{
      from:"user",
      let:{senderId:"$senderId"},
      pipeline:[{$match:{$expr:{$eq:["$$senderId","$_id"]}}}],
      as:"inventory"
  }};

  if(page == "user"){
    keyObj[1] = {$match:{"senderId":user._id}};
  }else{
    keyObj[1] = {$match:{$or:[{query:key},{"answers.answer":key},{"senderId":key}]}};
  }

  keyObj[2] = {$unwind:"$answers"};

  keyObj[3] = {$unwind:"$inventory"};

  keyObj[4] = {$group:{
    _id:"$_id",
    senderId:{$first:"$senderId"},
    query:{$first:"$query"},
    type:{$first:"$type"},
    answers:{$push:{
      answer:"$answers.answer",
      total:{$size:"$answers.voter"}
    }},
    answer:{$push:{
      answer:"$answers.answer"
    }},
    voters:{$first:"$voters"},
    comment:{$first:{$size:"$comment"}},
    favorite:{$first:"$favorite"},
    date:{$first:"$date"},
    inventory:{$first:"$inventory"}
  }};

  keyObj[5] = {$project:{
    _id:1,
    senderId:1,
    query:1,
    type:1,
    total:{$size:"$voters"},
    comment:1,
    favorite:{$size:"$favorite"},
    date:1,
    "inventory.nickname":1,
    "inventory.img":1,
    answers:{
      $cond:{
        if:{$in:[user._id,"$voters"]},
        then:"$answers",
        else:"$answer"
      }
    },
    result:{
      $cond:{
        if:{$in:[user._id,"$voters"]},
        then:true,
        else:false
      }
    },
    myfavorite:{
      $cond:{
        if:{$in:[user._id,"$favorite"]},
        then:true,
        else:false
      }
    }
  }};

  if(sort == "date"){
    keyObj[6] = {$sort:{date:order}};
  }else if(sort == "favorite"){
    keyObj[6] = {$sort:{favorite:order}};
  }else if(sort == "total"){
    keyObj[6] = {$sort:{total:order}};
  }

  keyObj[7] = {$skip:index};
  keyObj[8] = {$limit:15};

  return keyObj;
}
<file_sep>/webserver/lib/mongo.js
const Mongo = require('mongodb')
const MongoClient = Mongo.MongoClient;
const ip = require("../ip.js");
const url = 'mongodb://'+ ip.ip +':27017';

//callback関数に検索結果を適用する。
exports.aggregate = function(collectionName,key){
  return new Promise(function(resolve,reject){
    MongoClient.connect(url,{ useNewUrlParser:true },function(error, database) {
      if (error) reject(error);
      const dbo = database.db("Data");
      dbo.collection(collectionName).aggregate(key).toArray(function(err, result) {
        if (err) reject(err);
        database.close();
        resolve(result)
      });
    });
  })
  .catch(function(err){
    console.log(err);
  })
};

//ObjectをDBに挿入する
exports.insert = function(collection,key){
  return new Promise(function(resolve,reject){
    MongoClient.connect(url,{ useNewUrlParser:true },function(error, database) {
      if (error) reject(error);
      const dbo = database.db("Data");
      dbo.collection(collection).insertOne(key, function(err, result) {
        if (err) reject(err);
        database.close();
        resolve(result);
      });
    });
  })
  .catch(function(err){
    console.log(err);
  })
}

// userの認証
exports.userCheck = function(checkuser){
  if(!(checkuser._id && checkuser.sessionkey)){
    checkuser._id = "";
    checkuser.sessionkey = "";
  }
  return new Promise(function(resolve,reject){
    MongoClient.connect(url,{ useNewUrlParser:true },function(error, database) {
      if(error) reject(error);
      const dbo = database.db("Data");
      const key = [{$match:{_id:checkuser._id,sessionkey:checkuser.sessionkey}}];
      dbo.collection("user").aggregate(key).toArray(function(err, result) {
        if (err) reject(err);
        database.close();
        if(result[0]){
          resolve(result[0])
        }else {
          resolve(null);
        }
      });
    });
  })
  .catch(function(err){
    console.log(err);
  })
}

exports.ObjectID = Mongo.ObjectID;

exports.favorite = function (userId,targetId,favorite){
  let database = null;
  let Data = null;
  let targetObjId = Mongo.ObjectID(targetId);
  return new Promise((resolve,reject)=>{
    open().then((db)=>{
        database = db;
        return db.db("Data");
    })
    .then((data)=>{
        Data = data;
        if(favorite === 1){
          return Promise.all([
            Data.collection("question").updateOne({_id:targetObjId},{$addToSet:{"favorite":userId}}),
            Data.collection("user").updateOne({_id:userId},{$addToSet:{"favorite":targetId}})
          ])
        }else if(favorite === 0){
          return Promise.all([
            Data.collection("question").updateOne({_id:targetObjId},{$pull:{"favorite":userId}}),
            Data.collection("user").updateOne({_id:userId},{$pull:{"favorite":targetId}})
          ])
        }
    })
    .then(()=>{
      const matchKey = [
        {$match:{_id:targetObjId}},
        {$project:{favorite:{$size:"$favorite"}}}
      ];
      Data.collection("question").aggregate(matchKey).toArray((err,result)=>{
        if(err) reject(err);
        database.close()
        resolve({status:"success",favorite:result[0].favorite})
      })
    })
    .catch((err)=>{
      if(database) database.close();
      reject(err);
    })
  });
}
//投票
exports.vote = function (user,id,index){
  let database = null;
  let userCollection = null;
  const key = {_id:Mongo.ObjectID(id)};
  return new Promise((resolve,reject) => {
    open().then((db)=>{
        database = db;
        return db.db("Data").collection('question')
    })
    .then((users)=>{
        userCollection = users;
        let prom = Promise.resolve();
        for(let i = 0; i<index.length; i++){
          if(index[i] == "1"){
            let update = {$addToSet:{[`answers.${i}.voter`]: user._id}};
            prom = prom.then(()=>{
              return userCollection.updateOne(key,update);
            })
          }
        }
        return prom
    })
    .then(()=>{
        return userCollection.update(key,{$addToSet:{voters:user._id}})
    })
    .then(()=>{
        const matchKey = [
          {$match:key},
          {$unwind:"$answers"},
          {$group:{
            _id:"$_id",
            answers:{$push:{
              answer:"$answers.answer",
              "total":{$size:"$answers.voter"}
            }},
            total:{$first:{$size:"$voters"}},
            comment:{$first:{$size:"$comment"}},
            favorite:{$first:{$size:"$favorite"}},
          }}
        ]
        userCollection.aggregate(matchKey).toArray((err,result)=>{
          if(err) reject(err);
          database.close()
          resolve(result[0]);
        });
    })
    .catch((err)=>{
      if(database) database.close();
      reject(err);
    })
  })
}
function open(){
  return new Promise((resolve, reject)=>{
    MongoClient.connect(url,{ useNewUrlParser: true },(err, db) => {
      if (err) {
        reject(err);
      } else {
        resolve(db);
      }
    });
  });
}
// sessionkey挿入
exports.session = function(id,sessionkey){
  return new Promise(function(resolve,reject){
    MongoClient.connect(url,{ useNewUrlParser:true },function(error, database) {
      if (error) throw error;
      const dbo = database.db("Data");
      dbo.collection("user").update({_id:id},{$set:{'sessionkey':sessionkey}},
      function(err, res) {
        if (err) throw err;
        resolve();
      });
    });
  })
  .catch(function(err){
    console.log(err);
  })
}
