#include <bits/stdc++.h>
using namespace std;
int n, m, c, q;
unordered_map<int, unordered_map<int, int>> mp;
vector<unordered_set<int>> S, rS;
struct DSU {
  int n;
  vector<int> f, sz;
  DSU(int n_) {
    n = n_;
    f.resize(n);
    sz.resize(n);
    S.resize(n);
    rS.resize(n);
    for (int i = 0; i < n; ++i) {
      f[i] = i;
      sz[i] = 1;
      S[i] = {i};
      rS[i] = {i};
    }
  }
  int find_father(int a) {
    return f[a] == a ? a : (f[a] = this->find_father(f[a]));
  }
  inline int get_size(int i) { return sz[this->find_father(i)]; }
  bool connected(int a, int b) {
    return this->find_father(a) == this->find_father(b);
  }
  void connect(int a, int b) {
    if (connected(a, b)) return;
    a = f[a];
    b = f[b];
    if (sz[a] > sz[b]) swap(a, b);
    sz[b] += sz[a];
    f[a] = b;
    for (auto e : rS[a]) {
      S[e].erase(a);
      S[e].insert(b);
      rS[b].insert(e);
    }
    rS[a].clear();
  }
};
DSU dsu(1);
void insert_edge() {
  int x, y, z;
  cin >> x >> y >> z;
  for (auto u : {x, y})
    if (mp[u][z] == 0) {
      mp[u][z] = x + y - u;
    } else
      dsu.connect(mp[u][z], x + y - u);
  S[x].insert(dsu.find_father(y));
  rS[dsu.find_father(y)].insert(x);
  S[y].insert(dsu.find_father(x));
  rS[dsu.find_father(x)].insert(y);
}
int main() {
  cin >> n >> m >> c >> q;
  dsu = DSU(n + 1);
  while (m--) {
    insert_edge();
  }
  while (q--) {
    char ch;
    cin >> ch;
    if (ch == '+') {
      insert_edge();
    }
    if (ch == '?') {
      int x, y;
      cin >> x >> y;
      cout << (S[y].find(dsu.find_father(x)) != S[y].end() ? "Yes" : "No")
           << "\n";
    }
  }
}
