@Override
 public Set<Integer> getCategoryIdsByConceptId(int conceptId) {
   byte[] key = (prefix + "ctp:catids").getBytes(ENCODING);
   byte[] hkey = NumberUtils.int2Bytes(conceptId);
   byte[] value = jedis.hget(key, hkey);
   return NumberUtils.bytes2IntSet(value);
 }
 @Override
 public int getDepth(int catId) {
   byte[] key = (prefix + "id2depth").getBytes(ENCODING);
   byte[] hkey = NumberUtils.int2Bytes(catId);
   byte[] value = jedis.hget(key, hkey);
   return (value == null) ? -1 : NumberUtils.bytes2Int(value);
 }
  @Override
  public int getRecursiveConceptCount(int catId) {
    byte[] key = (prefix + "id2acr").getBytes(ENCODING);
    byte[] hkey = NumberUtils.int2Bytes(catId);

    byte[] value = jedis.hget(key, hkey);
    return (value == null) ? 0 : NumberUtils.bytes2Int(value);
  }
  @Override
  public Set<Integer> getChildIds(int catId) {
    byte[] key = (prefix + "id2cids").getBytes(ENCODING);
    byte[] hkey = NumberUtils.int2Bytes(catId);

    byte[] value = jedis.hget(key, hkey);
    Set<Integer> ids = NumberUtils.bytes2IntSet(value);
    return ids;
  }
  private void incRecursiveConceptCount(int catId, int count) {
    byte[] key = (prefix + "id2acr").getBytes(ENCODING);
    byte[] hkey = NumberUtils.int2Bytes(catId);

    byte[] value = jedis.hget(key, hkey);
    if (value == null) {
      jedis.hset(key, hkey, NumberUtils.int2Bytes(count));
    } else {
      int old = NumberUtils.bytes2Int(value);
      jedis.hset(key, hkey, NumberUtils.int2Bytes(count + old));
    }
  }
  private void saveChildren(int catId, int... childIds) {
    byte[] key = (prefix + "id2cids").getBytes(ENCODING);
    byte[] hkey = NumberUtils.int2Bytes(catId);
    byte[] value = jedis.hget(key, hkey);
    Set<Integer> ids = NumberUtils.bytes2IntSet(value);

    boolean changed = false;
    for (int id : childIds) {
      if (idExist(id) && !ids.contains(id)) {
        ids.add(id);
        changed = true;
      }
    }
    if (changed) jedis.hset(key, hkey, NumberUtils.intSet2Bytes(ids));
  }
  private void saveParents(int catId, int... parentIds) {
    byte[] key = (prefix + "id2pids").getBytes(ENCODING);
    byte[] hkey = NumberUtils.int2Bytes(catId);

    byte[] value = jedis.hget(key, hkey);
    Set<Integer> ids = NumberUtils.bytes2IntSet(value);

    boolean changed = false;
    for (int p : parentIds) {
      if (idExist(p)) {
        ids.add(p);
        changed = true;
      }
    }
    if (changed) jedis.hset(key, hkey, NumberUtils.intSet2Bytes(ids));
  }
  @Override
  public int getIdByName(String name, int valueForNotExisted) {
    byte[] key = (prefix + "name2id").getBytes(ENCODING);
    byte[] value = jedis.hget(key, name.toLowerCase().getBytes(ENCODING));

    return value == null ? valueForNotExisted : NumberUtils.bytes2Int(value);
  }
  @Override
  public Set<Integer> getParentIds(int catId) {
    byte[] key = (prefix + "id2pids").getBytes(ENCODING);
    byte[] hkey = NumberUtils.int2Bytes(catId);

    byte[] value = jedis.hget(key, hkey);
    Set<Integer> ids = NumberUtils.bytes2IntSet(value);

    // remove skipped categories:
    // 35321838 Wikipedia categories named after scientific buildings
    // 35321841 Wikipedia categories named after scientific organizations
    // 35321847 Wikipedia categories named after scientists
    ids.remove(35321838);
    ids.remove(35321841);
    ids.remove(35321847);
    return ids;
  }
 @Override
 public String getNameById(int pageId) {
   byte[] key = (prefix + "id2name").getBytes(ENCODING);
   byte[] value = jedis.hget(key, NumberUtils.int2Bytes(pageId));
   if (value == null) {
     return "Not Exist:" + pageId;
   }
   return new String(value, ENCODING);
 }
  @Override
  public void saveNameIdMapping(String name, int pageId) {
    if (StringUtils.isEmpty(name)) {
      return;
    }
    if (nameExist(name) || idExist(pageId)) {
      LOG.warn(pageId + "->" + name + " has already existed.");
      return;
    }

    // save name->id mapping
    byte[] key = (prefix + "name2id").getBytes(ENCODING);
    jedis.hset(key, name.toLowerCase().getBytes(ENCODING), NumberUtils.int2Bytes(pageId));

    // save id->name mapping
    key = (prefix + "id2name").getBytes(ENCODING);
    jedis.hset(key, NumberUtils.int2Bytes(pageId), name.getBytes(ENCODING));
  }
  @Override
  public int getIdByName(String name) throws MissedException {
    byte[] key = (prefix + "name2id").getBytes(ENCODING);
    byte[] value = jedis.hget(key, name.toLowerCase().getBytes(ENCODING));

    if (value == null) {
      throw new MissedException("wiki category " + name + " does not exist");
    }
    return NumberUtils.bytes2Int(value);
  }
  private void saveChildren(int catId, String... children) {
    byte[] key = (prefix + "id2cids").getBytes(ENCODING);
    byte[] hkey = NumberUtils.int2Bytes(catId);

    byte[] value = jedis.hget(key, hkey);
    Set<Integer> ids = NumberUtils.bytes2IntSet(value);

    boolean changed = false;
    for (String c : children) {
      if (nameExist(c)) {
        try {
          ids.add(getIdByName(c));
          changed = true;
        } catch (MissedException e) {
          e.printStackTrace();
        }
      }
    }
    if (changed) jedis.hset(key, hkey, NumberUtils.intSet2Bytes(ids));
  }
  private void saveParents(int catId, Collection<String> parents) {
    byte[] key = (prefix + "id2pids").getBytes(ENCODING);
    byte[] hkey = NumberUtils.int2Bytes(catId);

    byte[] value = jedis.hget(key, hkey);
    Set<Integer> ids = NumberUtils.bytes2IntSet(value);

    boolean changed = false;
    for (String p : parents) {
      if (nameExist(p)) {
        try {
          ids.add(getIdByName(p));
          changed = true;
        } catch (MissedException e) {
          LOG.warn(e.toString());
        }
      }
    }
    if (changed) jedis.hset(key, hkey, NumberUtils.intSet2Bytes(ids));
  }
  @Override
  public void saveConceptRelation(int catId, int conceptId) {
    byte[] key = (prefix + "id2aids").getBytes(ENCODING);
    byte[] hkey = NumberUtils.int2Bytes(catId);
    byte[] value = jedis.hget(key, hkey);
    Set<Integer> ids = NumberUtils.bytes2IntSet(value);

    if (!ids.contains(conceptId)) {
      ids.add(conceptId);
      jedis.hset(key, hkey, NumberUtils.intSet2Bytes(ids));
    }

    // 记录该类别下概念的数量
    incConceptCount(catId, 1);

    // 记录概念所隶属的类别
    key = (prefix + "ctp:catids").getBytes(ENCODING);
    hkey = NumberUtils.int2Bytes(conceptId);
    value = jedis.hget(key, hkey);
    ids = NumberUtils.bytes2IntSet(value);
    if (!ids.contains(catId)) {
      ids.add(catId);
      jedis.hset(key, hkey, NumberUtils.intSet2Bytes(ids));
    }
  }
 @Override
 public String getNameById(int id, String defaultValue) {
   byte[] key = (prefix + "id2name").getBytes(ENCODING);
   byte[] value = jedis.hget(key, NumberUtils.int2Bytes(id));
   return (value == null) ? defaultValue : new String(value, ENCODING);
 }
 @Override
 public boolean idExist(int pageId) {
   byte[] key = (prefix + "id2name").getBytes(ENCODING);
   return jedis.hexists(key, NumberUtils.int2Bytes(pageId));
 }
  private void saveDepth(int catId, int depth) {
    byte[] key = (prefix + "id2depth").getBytes(ENCODING);
    byte[] hkey = NumberUtils.int2Bytes(catId);

    jedis.hset(key, hkey, NumberUtils.int2Bytes(depth));
  }