ืจื™ืคึผืœื™ื™ืกื™ื ื’ EAV ืžื™ื˜ JSONB ืื™ืŸ PostgreSQL

TL; DR: JSONB ืงืขื ืขืŸ ื–ื™ื™ืขืจ ืคืึทืจืคึผืึธืฉืขื˜ืขืจืŸ ื“ื™ ืึทื ื˜ื•ื•ื™ืงืœื•ื ื’ ืคื•ืŸ ื“ืึทื˜ืึทื‘ื™ื™ืก ืกื˜ืฉืขืžืึท ืึธืŸ ืกืึทืงืจืึทืคื™ื™ืกื™ื ื’ ืึธื ืคึฟืจืขื’ ืคืึธืจืฉื˜ืขืœื•ื ื’.

ื”ืงื“ืžื”

ืœืึธืžื™ืจ ื’ืขื‘ืŸ ืึท ืงืœืึทืกื™ืฉ ื‘ื™ื™ึทืฉืคึผื™ืœ ืคื•ืŸ ืžื™ืกื˜ืึธืžืข ืื™ื™ื ืขืจ ืคื•ืŸ ื“ื™ ืึธื•ืœื“ืึทืกื˜ ื ื•ืฆืŸ ืงืึทืกืขืก ืื™ืŸ ื“ืขืจ ื•ื•ืขืœื˜ ืคื•ืŸ ืึท ืจื™ืœื™ื™ืฉืึทื ืึทืœ ื“ืึทื˜ืึทื‘ื™ื™ืก (ื“ืึทื˜ืึทื‘ื™ื™ืก): ืžื™ืจ ื”ืึธื‘ืŸ ืึทืŸ ืขื ื˜ื™ื˜ื™, ืื•ืŸ ืžื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ืจืึทื˜ืขื•ื•ืขืŸ ื–ื™ื›ืขืจ ืคึผืจืึธืคึผืขืจื˜ื™ืขืก (ืึทื˜ืจื™ื‘ื™ืึทืฅ) ืคื•ืŸ ื“ืขื ืขื ื˜ื™ื˜ื™. ืึธื‘ืขืจ ื ื™ื˜ ืึทืœืข ื™ื ืกื˜ืึทื ืกื™ื– ืงืขืŸ ื”ืึธื‘ืŸ ื“ื™ ื–ืขืœื‘ืข ื’ืึทื ื’ ืคื•ืŸ ืคึผืจืึธืคึผืขืจื˜ื™ืขืก, ืื•ืŸ ืžืขืจ ืคึผืจืึธืคึผืขืจื˜ื™ืขืก ืงืขืŸ ื–ื™ื™ืŸ ืฆื•ื’ืขื’ืขื‘ืŸ ืื™ืŸ ื“ืขืจ ืฆื•ืงื•ื ืคึฟื˜.

ื“ื™ ื™ื–ื™ืึทืกื˜ ื•ื•ืขื’ ืฆื• ืกืึธืœื•ื•ืข ื“ืขื ืคึผืจืึธื‘ืœืขื ืื™ื– ืฆื• ืฉืึทืคึฟืŸ ืึท ื–ื™ื™ึทืœ ืื™ืŸ ื“ื™ ื“ืึทื˜ืึทื‘ื™ื™ืก ื˜ื™ืฉ ืคึฟืึทืจ ื™ืขื“ืขืจ ืคืึทืจืžืึธื’ ื•ื•ืขืจื˜, ืื•ืŸ ืคืฉื•ื˜ ืคึผืœืึธืžื‘ื™ืจืŸ ื“ื™ ื•ื•ืึธืก ื–ืขื ืขืŸ ื“ืืจืฃ ืคึฟืึทืจ ืึท ืกืคึผืขืฆื™ืคื™ืฉ ืขื ื˜ื™ื˜ื™ ื‘ื™ื™ึทืฉืคึผื™ืœ. ื’ืจื•ื™ืก! ืคึผืจืึธื‘ืœืขื ืกืึทืœื•ื•ื“ ... ื‘ื™ื– ื“ื™ื™ืŸ ื˜ื™ืฉ ื›ึผื•ืœืœ ืžื™ืœื™ืึทื ื– ืคื•ืŸ ืจืขืงืึธืจื“ืก ืื•ืŸ ืื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ืœื™ื™ื’ืŸ ืึท ื ื™ื™ึทืข ืจืขืงืึธืจื“.

ื‘ืึทื˜ืจืึทื›ื˜ืŸ ื“ื™ EAV ืžื•ืกื˜ืขืจ (ืขื ื˜ื™ื˜ื™-ืึทื˜ืจื™ื‘ื™ื•ื˜-ื•ื•ืขืจื˜), ืขืก ืึทืงืขืจื– ื’ืึทื ืฅ ืึธืคื˜. ืื™ื™ืŸ ื˜ื™ืฉ ื›ึผื•ืœืœ ืขื ื˜ื™ื˜ื™ื– (ืจืขืงืึธืจื“ืก), ืืŸ ืื ื“ืขืจ ื˜ื™ืฉ ื›ึผื•ืœืœ ืคืึทืจืžืึธื’ ื ืขืžืขืŸ (ืึทื˜ื˜ืจืึทื‘ื™ื•ืฅ), ืื•ืŸ ืึท ื“ืจื™ื˜ ื˜ื™ืฉ ืึทืกืกืึธืกื™ืึทื˜ืขืก ืขื ื˜ื™ื˜ื™ื– ืžื™ื˜ ื–ื™ื™ืขืจ ืึทื˜ืจื™ื‘ื™ื•ืฅ ืื•ืŸ ื›ึผื•ืœืœ ื“ื™ ื•ื•ืขืจื˜ ืคื•ืŸ ื“ื™ ืึทื˜ืจื™ื‘ื™ื•ืฅ ืคึฟืึทืจ ื“ื™ ืงืจืึทื ื˜ ืขื ื˜ื™ื˜ื™. ื“ืึธืก ื’ื™ื˜ ืื™ืจ ื“ื™ ืคื™ื™ื™ืงื™ื™ื˜ ืฆื• ื”ืึธื‘ืŸ ืคืึทืจืฉื™ื“ืขื ืข ืกืขืฅ ืคื•ืŸ ืคึผืจืึธืคึผืขืจื˜ื™ืขืก ืคึฟืึทืจ ืคืึทืจืฉื™ื“ืขื ืข ืึทื‘ื“ื–ืฉืขืงืฅ, ืื•ืŸ ืื•ื™ืš ืœื™ื™ื’ืŸ ืคึผืจืึธืคึผืขืจื˜ื™ืขืก ืื•ื™ืฃ ื“ื™ ืคืœื™ืขืŸ ืึธืŸ ื˜ืฉืึทื ื’ื™ื ื’ ื“ื™ ื“ืึทื˜ืึทื‘ื™ื™ืก ืกื˜ืจื•ืงื˜ื•ืจ.

ืึธื‘ืขืจ, ืื™ืš ื•ื•ืึธืœื˜ ื ื™ืฉื˜ ืฉืจื™ื™ึทื‘ืŸ ื“ืขื ืคึผืึธืกื˜ืŸ ืื•ื™ื‘ ืขืก ื–ืขื ืขืŸ ื ื™ืฉื˜ ืขื˜ืœืขื›ืข ื“ืึทื•ื ืกื™ื™ื“ื– ืฆื• ื“ื™ EVA ืฆื•ื’ืึทื ื’. ืึทื–ื•ื™, ืคึฟืึทืจ ื‘ื™ื™ึทืฉืคึผื™ืœ, ืฆื• ืงืจื™ื’ืŸ ืื™ื™ื ืขืจ ืึธื“ืขืจ ืžืขืจ ืขื ื˜ื™ื˜ื™ื– ื•ื•ืึธืก ื”ืึธื‘ืŸ 1 ืึทื˜ืจื™ื‘ื™ื•ื˜ ื™ืขื“ืขืจ, 2 ื“ื–ืฉื•ื™ื ืฅ ื–ืขื ืขืŸ ืคืืจืœืื ื’ื˜ ืื™ืŸ ื“ื™ ืึธื ืคึฟืจืขื’: ื“ืขืจ ืขืจืฉื˜ืขืจ ืื™ื– ืึท ืคืึทืจื‘ื™ื ื“ืŸ ืžื™ื˜ ื“ื™ ืึทื˜ืจื™ื‘ื™ื•ื˜ ื˜ื™ืฉ, ื“ื™ ืจื’ืข ืื™ื– ืึท ืคืึทืจื‘ื™ื ื“ืŸ ืžื™ื˜ ื“ื™ ื•ื•ืึทืœื•ืขืก ื˜ื™ืฉ. ืื•ื™ื‘ ืึท ืขื ื˜ื™ื˜ื™ ื”ืื˜ 2 ืึทื˜ืจื™ื‘ื™ื•ืฅ, 4 ื“ื–ืฉื•ื™ื ืฅ ื–ืขื ืขืŸ ื“ืืจืฃ! ืึทื“ื“ื™ื˜ื™ืึธื ืึทืœืœื™, ืึทืœืข ืึทื˜ืจื™ื‘ื™ื•ืฅ ื–ืขื ืขืŸ ื˜ื™ืคึผื™ืงืœื™ ืกื˜ืึธืจื“ ื•ื•ื™ ืกื˜ืจื™ื ื’ืก, ื•ื•ืึธืก ืจืขื–ื•ืœื˜ืื˜ืŸ ืื™ืŸ ื˜ื™ืคึผ ืงืึทืกื˜ื™ื ื’ ืคึฟืึทืจ ื“ื™ ืจืขื–ื•ืœื˜ืึทื˜ ืื•ืŸ ื“ื™ WHERE ืคึผื•ื ืงื˜. ืื•ื™ื‘ ืื™ืจ ืฉืจื™ื™ึทื‘ืŸ ืึท ืคึผืœืึทืฅ ืคื•ืŸ ืคึฟืจืื’ืŸ, ื“ืึธืก ืื™ื– ื’ืึทื ืฅ ื•ื•ื™ืกื˜ืคืึทืœ ืื™ืŸ ื˜ืขืจืžื™ื ืขืŸ ืคื•ืŸ ืžื™ื˜ืœ ื ื•ืฆืŸ.

ื˜ืจืึธืฅ ื“ื™ ืงืœืึธืจ ื•ื•ื™ ื“ืขืจ ื˜ืึธื’ ื›ื™ืกืึธืจืŸ, EAV ืื™ื– ืœืึทื ื’ ืฉื•ื™ืŸ ื’ืขื ื™ืฆื˜ ืฆื• ืกืึธืœื•ื•ืข ื“ื™ ื˜ื™ื™ืคึผืก ืคื•ืŸ ืคึผืจืึธื‘ืœืขืžืก. ื“ืืก ื–ืขื ืขืŸ ื’ืขื•ื•ืขืŸ ื‘ืึทืฉืขืจื˜ ืฉืึธืจื˜ืงืึธืžื™ื ื’ืก, ืื•ืŸ ืขืก ืื™ื– ืคืฉื•ื˜ ืงื™ื™ืŸ ื‘ืขืกืขืจ ืื ื“ืขืจ ื‘ืจื™ืจื”.
ืึธื‘ืขืจ ื“ืขืžืึธืœื˜ ืึท ื ื™ื™ึทืข "ื˜ืขื›ื ืึธืœืึธื’ื™ืข" ืื™ื– ืืจื•ื™ืก ืื™ืŸ PostgreSQL ...

ืกื˜ืึทืจื˜ื™ื ื’ ืžื™ื˜ PostgreSQL 9.4, ื“ื™ JSONB ื“ืึทื˜ืŸ ื˜ื™ืคึผ ืื™ื– ืฆื•ื’ืขื’ืขื‘ืŸ ืฆื• ืงืจืึธื JSON ื‘ื™ื™ื ืขืจื™ ื“ืึทื˜ืŸ. ื›ืึธื˜ืฉ ืกื˜ืึธืจื™ื ื’ JSON ืื™ืŸ ื“ืขื ืคึฟืึธืจืžืึทื˜ ื˜ื™ืคึผื™ืงืœื™ ื ืขืžื˜ ืึท ื‘ื™ืกืœ ืžืขืจ ืคึผืœืึทืฅ ืื•ืŸ ืฆื™ื™ื˜ ื•ื•ื™ ืงืœืึธืจ ื˜ืขืงืกื˜ JSON, ื“ื•ืจื›ืคื™ืจืŸ ืึทืคึผืขืจื™ื™ืฉืึทื ื– ืื•ื™ืฃ ืขืก ืื™ื– ืคื™ืœ ืคืึทืกื˜ืขืจ. JSONB ืื•ื™ืš ืฉื˜ื™ืฆื˜ ื™ื ื“ืขืงืกื™ื ื’, ื•ื•ืึธืก ืžืื›ื˜ ืคึฟืจืื’ืŸ ืืคื™ืœื• ืคืึทืกื˜ืขืจ.

ื“ื™ JSONB ื“ืึทื˜ืŸ ื˜ื™ืคึผ ืึทืœืึทื•ื– ืื•ื ื“ื– ืฆื• ืคืึทืจื‘ื™ื™ึทื˜ืŸ ื“ื™ ืงืึทืžื‘ืขืจืกืึทื EAV ืžื•ืกื˜ืขืจ ื“ื•ืจืš ืึทื“ื™ื ื’ ื‘ืœื•ื™ื– ืื™ื™ืŸ JSONB ื–ื™ื™ึทืœ ืฆื• ืื•ื ื“ื–ืขืจ ืขื ื˜ื™ื˜ื™ ื˜ื™ืฉ, ืื•ืŸ ื–ื™ื™ืขืจ ืกื™ืžืคึผืœืึทืคื™ื™ื™ื ื’ ื“ืึทื˜ืึทื‘ื™ื™ืก ืคึผืœืึทืŸ. ืึธื‘ืขืจ ืคื™ืœืข ื˜ืขื ื”ืŸ ืึทื– ื“ืึธืก ื–ืึธืœ ื–ื™ื™ืŸ ื‘ืื’ืœื™ื™ื˜ ืžื™ื˜ ืึท ืคืึทืจืงืœืขื ืขืจืŸ ืื™ืŸ ืคึผืจืึธื•ื“ืึทืงื˜ื™ื•ื•ืึทื˜ื™... ื“ืขืจืคึฟืึทืจ ื”ืึธื‘ ืื™ืš ื’ืขืฉืจื™ื‘ืŸ ื“ืขื ืึทืจื˜ื™ืงืœ.

ื‘ืึทืฉื˜ืขื˜ื™ืงืŸ ืึท ืคึผืจืึธื‘ืข ื“ืึทื˜ืึทื‘ื™ื™ืก

ืคึฟืึทืจ ื“ืขื ืคืึทืจื’ืœื™ื™ึทืš, ืื™ืš ื‘ืืฉืืคืŸ ื“ื™ ื“ืึทื˜ืึทื‘ื™ื™ืก ืื•ื™ืฃ ืึท ืคืจื™ืฉ ื™ื ืกื˜ืึทืœื™ืจื•ื ื’ ืคื•ืŸ PostgreSQL 9.5 ืื•ื™ืฃ ื“ื™ ื‘ื•ื™ืขืŸ ืคื•ืŸ $ 80 ื“ื™ื’ื™ื˜ืึทืœืึธืกืขืึทืŸ Ubuntu 14.04. ื ืึธืš ื‘ืึทืฉื˜ืขื˜ื™ืงืŸ ืขื˜ืœืขื›ืข ืคึผืึทืจืึทืžืขื˜ืขืจืก ืื™ืŸ postgresql.conf ืื™ืš ื’ืขืœืืคืŸ this ืฉืจื™ืคื˜ ื ื™ืฆืŸ psql. ื“ื™ ืคืืœื’ืขื ื“ืข ื˜ื™ืฉืŸ ื–ืขื ืขืŸ ื‘ืืฉืืคืŸ ืฆื• ืคืึธืจืฉื˜ืขืœืŸ ื“ื™ ื“ืึทื˜ืŸ ืื™ืŸ EAV ืคืึธืจืขื:

CREATE TABLE entity ( 
  id           SERIAL PRIMARY KEY, 
  name         TEXT, 
  description  TEXT
);
CREATE TABLE entity_attribute (
  id          SERIAL PRIMARY KEY, 
  name        TEXT
);
CREATE TABLE entity_attribute_value (
  id                  SERIAL PRIMARY KEY, 
  entity_id           INT    REFERENCES entity(id), 
  entity_attribute_id INT    REFERENCES entity_attribute(id), 
  value               TEXT
);

ื•ื ื˜ืขืจ ืื™ื– ืึท ื˜ื™ืฉ ื•ื•ื• ื“ื™ ื–ืขืœื‘ืข ื“ืึทื˜ืŸ ื•ื•ืขื˜ ื–ื™ื™ืŸ ืกื˜ืึธืจื“, ืึธื‘ืขืจ ืžื™ื˜ ืึทื˜ืจื™ื‘ื™ื•ืฅ ืื™ืŸ ืึท JSONB ื˜ื™ืคึผ ื–ื™ื™ึทืœ - ืคึผืจืึธืคึผืขืจื˜ื™ืขืก.

CREATE TABLE entity_jsonb (
  id          SERIAL PRIMARY KEY, 
  name        TEXT, 
  description TEXT,
  properties  JSONB
);

ืขืก ืงื•ืงื˜ ืคื™ืœ ืกื™ืžืคึผืœืขืจ, ืฆื™ ื ื™ื˜? ื“ืขืจื ืึธืš ืขืก ืื™ื– ืฆื•ื’ืขื’ืขื‘ืŸ ืฆื• ื“ื™ ืขื ื˜ื™ื˜ื™ ื˜ื™ืฉืŸ (ืขื ื˜ื™ื˜ื™ & entity_jsonb) 10 ืžื™ืœื™ืึธืŸ ืจืขืงืึธืจื“ืก, ืื•ืŸ ืึทืงืึธืจื“ื™ื ื’ืœื™, ื“ื™ ื˜ื™ืฉ ืื™ื– ื’ืขื•ื•ืขืŸ ืึธื ื’ืขืคื™ืœื˜ ืžื™ื˜ ื“ื™ ื–ืขืœื‘ืข ื“ืึทื˜ืŸ ื ื™ืฆืŸ ื“ื™ EAV ืžื•ืกื˜ืขืจ ืื•ืŸ ื“ืขืจ ืฆื•ื’ืึทื ื’ ืžื™ื˜ ืึท JSONB ื–ื™ื™ึทืœ - entity_jsonb.properties. ืื–ื•ื™, ืžื™ืจ ื‘ืืงื•ืžืขืŸ ืขื˜ืœืขื›ืข ืคืึทืจืฉื™ื“ืขื ืข ื“ืึทื˜ืŸ ื˜ื™ื™ืคึผืก ืฆื•ื•ื™ืฉืŸ ื“ื™ ื’ืื ืฆืข ื’ืึทื ื’ ืคื•ืŸ ืคึผืจืึธืคึผืขืจื˜ื™ืขืก. ื‘ื™ื™ึทืฉืคึผื™ืœ ื“ืึทื˜ืŸ:

{
  id:          1
  name:        "Entity1"
  description: "Test entity no. 1"
  properties:  {
    color:        "red"
    lenght:       120
    width:        3.1882420
    hassomething: true
    country:      "Belgium"
  } 
}

ืึทื–ื•ื™ ืื™ืฆื˜ ืžื™ืจ ื”ืึธื‘ืŸ ื“ื™ ื–ืขืœื‘ืข ื“ืึทื˜ืŸ ืคึฟืึทืจ ื‘ื™ื™ื“ืข ืึธืคึผืฆื™ืขืก. ื–ืืœ ืก ืึธื ื”ื™ื™ื‘ืŸ ืงืึทืžืคึผืขืจื™ื ื’ ื™ืžืคึผืœืึทืžืึทื ืฅ ืื™ืŸ ืึทืจื‘ืขื˜!

ืคืึทืจืคึผืึธืฉืขื˜ืขืจืŸ ื“ื™ื™ืŸ ืคึผืœืึทืŸ

ืขืก ืื™ื– ื’ืขื•ื•ืขืŸ ืคืจื™ืขืจ ืกื˜ื™ื™ื˜ื™ื“ ืึทื– ื“ื™ ื“ืึทื˜ืึทื‘ื™ื™ืก ืคึผืœืึทืŸ ืื™ื– ื’ืขื•ื•ืขืŸ ื–ื™ื™ืขืจ ืกื™ืžืคึผืœืึทืคื™ื™ื“: ืื™ื™ืŸ ื˜ื™ืฉ, ื“ื•ืจืš ื ื™ืฆืŸ ืึท JSONB ื–ื™ื™ึทืœ ืคึฟืึทืจ ืคึผืจืึธืคึผืขืจื˜ื™ืขืก, ืึทื ืฉื˜ืึธื˜ ืคื•ืŸ ื ื™ืฆืŸ ื“ืจื™ื™ ื˜ื™ืฉืŸ ืคึฟืึทืจ EAV. ืื‘ืขืจ ื•ื•ื™ ืื™ื– ื“ืึธืก ืฉืคื™ื’ืœื˜ ืื™ืŸ ืจื™ืงื•ื•ืขืก? ื“ืขืจื”ื™ื™ึทื ื˜ื™ืงื•ื ื’ืขืŸ ืคื•ืŸ ืื™ื™ืŸ ืขื ื˜ื™ื˜ื™ ืคืึทืจืžืึธื’ ืงื•ืงื˜ ื•ื•ื™ ื“ืึธืก:

-- EAV
UPDATE entity_attribute_value 
SET value = 'blue' 
WHERE entity_attribute_id = 1 
  AND entity_id = 120;

-- JSONB
UPDATE entity_jsonb 
SET properties = jsonb_set(properties, '{"color"}', '"blue"') 
WHERE id = 120;

ื•ื•ื™ ืื™ืจ ืงืขื ืขืŸ ื–ืขืŸ, ื“ื™ ืœืขืฆื˜ืข ื‘ืงืฉื” ืื™ื– ื ื™ืฉื˜ ืกื™ืžืคึผืœืขืจ. ืฆื• ื“ืขืจื”ื™ื™ึทื ื˜ื™ืงืŸ ื“ื™ ื•ื•ืขืจื˜ ืคื•ืŸ ืึท ืคืึทืจืžืึธื’ ืื™ืŸ ืึท JSONB ื›ื™ื™ืคืขืฅ ืžื™ืจ ื”ืึธื‘ืŸ ืฆื• ื ื•ืฆืŸ ื“ื™ ืคึฟื•ื ืงืฆื™ืข jsonb_set(), ืื•ืŸ ื–ืึธืœ ืคืึธืจืŸ ืื•ื ื“ื–ืขืจ ื ื™ื™ึท ื•ื•ืขืจื˜ ื•ื•ื™ ืึท JSONB ื›ื™ื™ืคืขืฅ. ืึธื‘ืขืจ, ืžื™ืจ ื˜ืึธืŸ ื ื™ื˜ ื“ืึทืจืคึฟืŸ ืฆื• ื•ื•ื™ืกืŸ ืงื™ื™ืŸ ื™ื“ืขื ื˜ื™ืคื™ืขืจ ืื™ืŸ ืฉื˜ื™ื™ึทื’ืŸ. ืงื•ืง ืื™ืŸ ื“ื™ EAV ื‘ื™ื™ืฉืคึผื™ืœ, ืžื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ื•ื•ื™ืกืŸ ื‘ื™ื™ื“ืข ื“ื™ entity_id ืื•ืŸ ื“ื™ entity_attribute_id ืฆื• ื“ื•ืจื›ืคื™ืจืŸ ื“ื™ ื“ืขืจื”ื™ื™ึทื ื˜ื™ืงืŸ. ืื•ื™ื‘ ืื™ืจ ื•ื•ื™ืœืŸ ืฆื• ื“ืขืจื”ื™ื™ึทื ื˜ื™ืงืŸ ืึท ืคืึทืจืžืึธื’ ืื™ืŸ ืึท JSONB ื–ื™ื™ึทืœ ื‘ืื–ื™ืจื˜ ืื•ื™ืฃ ื“ื™ ื›ื™ื™ืคืขืฅ ื ืึธืžืขืŸ, ื“ืึธืก ืื™ื– ืึทืœืข ื’ืขื˜ืืŸ ืื™ืŸ ืื™ื™ืŸ ืคึผืฉื•ื˜ ืฉื•ืจื”.

ืื™ืฆื˜ ืœืึธื–ืŸ ืื•ื ื“ื– ืื•ื™ืกืงืœื™ื™ึทื‘ืŸ ื“ื™ ืขื ื˜ื™ื˜ื™ ื•ื•ืึธืก ืžื™ืจ ืคึผื•ื ืงื˜ ื“ืขืจื”ื™ื™ึทื ื˜ื™ืงื˜ ื‘ืื–ื™ืจื˜ ืื•ื™ืฃ ื–ื™ื™ืŸ ื ื™ื™ึทืข ืงืึธืœื™ืจ:

-- EAV
SELECT e.name 
FROM entity e 
  INNER JOIN entity_attribute_value eav ON e.id = eav.entity_id
  INNER JOIN entity_attribute ea ON eav.entity_attribute_id = ea.id
WHERE ea.name = 'color' AND eav.value = 'blue';

-- JSONB
SELECT name 
FROM entity_jsonb 
WHERE properties ->> 'color' = 'blue';

ืื™ืš ื˜ืจืึทื›ื˜ืŸ ืžื™ืจ ืงืขื ืขืŸ ืฉื˜ื™ืžืขืŸ ืึทื– ื“ื™ ืจื’ืข ืื™ื™ื ืขืจ ืื™ื– ืงื™ืจืฆืขืจ (ืงื™ื™ืŸ ืคืึทืจื‘ื™ื ื“ืŸ!), ืื•ืŸ ื“ืขืจื™ื‘ืขืจ ืžืขืจ ืœื™ื™ื ืขื•ื•ื“ื™ืง. JSONB ื•ื•ื™ื ืก ื“ืึธ! ืžื™ืจ ื ื•ืฆืŸ ื“ื™ JSON ->> ืึธืคึผืขืจืึทื˜ืึธืจ ืฆื• ื‘ืึทืงื•ืžืขืŸ ื“ื™ ืงืึธืœื™ืจ ื•ื•ื™ ืึท ื˜ืขืงืกื˜ ื•ื•ืขืจื˜ ืคื•ืŸ ืึท JSONB ื›ื™ื™ืคืขืฅ. ืขืก ืื™ื– ืื•ื™ืš ืึท ืฆื•ื•ื™ื™ื˜ ื•ื•ืขื’ ืฆื• ื“ืขืจื’ืจื™ื™ื›ืŸ ื“ื™ ื–ืขืœื‘ืข ืจืขื–ื•ืœื˜ืึทื˜ ืื™ืŸ ื“ื™ JSONB ืžืึธื“ืขืœ ื ื™ืฆืŸ ื“ื™ @> ืึธืคึผืขืจืึทื˜ืึธืจ:

-- JSONB 
SELECT name 
FROM entity_jsonb 
WHERE properties @> '{"color": "blue"}';

ื“ืึธืก ืื™ื– ืึท ื‘ื™ืกืœ ืžืขืจ ืงืึธืžืคึผืœื™ืฆื™ืจื˜: ืžื™ืจ ืงืึธื ื˜ืจืึธืœื™ืจืŸ ืื•ื™ื‘ ื“ื™ JSON ื›ื™ื™ืคืขืฅ ืื™ืŸ ื–ื™ื™ึทืŸ ืคึผืจืึธืคึผืขืจื˜ื™ืขืก ื–ื™ื™ึทืœ ื›ึผื•ืœืœ ืึท ื›ื™ื™ืคืขืฅ ื•ื•ืึธืก ืื™ื– ืจืขื›ื˜ ืคื•ืŸ ื“ื™ @> ืึธืคึผืขืจืึทื˜ืึธืจ. ื•ื•ื™ื™ื ื™ืงืขืจ ืœื™ื™ื ืขื•ื•ื“ื™ืง, ืžืขืจ ืคึผืจืึธื“ื•ืงื˜ื™ื•ื• (ื–ืขืŸ ื•ื•ื™ื™ื˜ืขืจ).

ืœืึธืžื™ืจ ืžืึทื›ืŸ ื“ื™ ื ื•ืฆืŸ ืคื•ืŸ JSONB ืืคื™ืœื• ื’ืจื™ื ื’ืขืจ ื•ื•ืขืŸ ืื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ืกืขืœืขืงื˜ื™ืจืŸ ืงื™ื™ืคืœ ืคึผืจืึธืคึผืขืจื˜ื™ืขืก ืื™ืŸ ืึทืžืึธืœ. ื“ืึธืก ืื™ื– ื•ื•ื• ื“ื™ JSONB ืฆื•ื’ืึทื ื’ ื˜ืึทืงืข ืงื•ืžื˜ ืื™ืŸ: ืžื™ืจ ืคืฉื•ื˜ ืื•ื™ืกืงืœื™ื™ึทื‘ืŸ ืคึผืจืึธืคึผืขืจื˜ื™ืขืก ื•ื•ื™ ื ืึธืš ืฉืคืืœื˜ืŸ ืื™ืŸ ืื•ื ื“ื–ืขืจ ืจืขื–ื•ืœื˜ืึทื˜ ืฉื˜ืขืœืŸ ืึธืŸ ื“ื™ ื ื•ื™ื˜ ืคึฟืึทืจ ื“ื–ืฉื•ื™ื ืฅ:

-- JSONB 
SELECT name
  , properties ->> 'color'
  , properties ->> 'country'
FROM entity_jsonb 
WHERE id = 120;

ืžื™ื˜ EAV ืื™ืจ ื•ื•ืขื˜ ื“ืึทืจืคึฟืŸ 2 ื“ื–ืฉื•ื™ื ืฅ ืคึฟืึทืจ ื™ืขื“ืขืจ ืคืึทืจืžืึธื’ ืื™ืจ ื•ื•ื™ืœืŸ ืฆื• ืคืจืขื’ืŸ. ืื™ืŸ ืžื™ื™ืŸ ืžื™ื™ื ื•ื ื’, ื“ื™ ืื•ื™ื‘ืŸ ืงื•ื•ื™ืจื™ื– ื•ื•ื™ื™ึทื–ืŸ ืึท ื’ืจื•ื™ืก ืกื™ืžืคึผืœืึทืคื™ืงื™ื™ืฉืึทืŸ ืื™ืŸ ื“ืึทื˜ืึทื‘ื™ื™ืก ืคึผืœืึทืŸ. ื–ืขืŸ ืžืขืจ ื‘ื™ื™ืฉืคื™ืœืŸ ืคื•ืŸ ื•ื•ื™ ืฆื• ืฉืจื™ื™ึทื‘ืŸ JSONB ืงื•ื•ื™ืจื™ื–, ืื•ื™ืš ืื™ืŸ this ืคึผืึธืกื˜ืŸ.
ืื™ืฆื˜ ืขืก ืื™ื– ืฆื™ื™ื˜ ืฆื• ืจืขื“ืŸ ื•ื•ืขื’ืŸ ืคืึธืจืฉื˜ืขืœื•ื ื’.

ืคืึธืจืฉื˜ืขืœื•ื ื’

ืฆื• ืคืึทืจื’ืœื™ื™ึทื›ืŸ ืคืึธืจืฉื˜ืขืœื•ื ื’ ืื™ืš ื’ืขื•ื•ื™ื™ื ื˜ ื“ืขืจืงืœืขืจืŸ ืึทื ืึทืœื™ืกื™ืก ืื™ืŸ ืงื•ื•ื™ืจื™ื–, ืฆื• ืจืขื›ืขื ืขืŸ ื“ื™ ื“ื•ืจื›ืคื™ืจื•ื ื’ ืฆื™ื™ื˜. ื™ืขื“ืขืจ ืึธื ืคึฟืจืขื’ ืื™ื– ืขืงืกืึทืงื™ื•ื˜ืึทื“ ื‘ื™ื™ึท ืžื™ื ื“ืกื˜ืขืจ ื“ืจื™ื™ ืžืึธืœ ื•ื•ื™ื™ึทืœ ื“ื™ ืึธื ืคึฟืจืขื’ ืคึผืœืึทื ื ืขืจ ื ืขืžื˜ ืžืขืจ ื“ื™ ืขืจืฉื˜ืขืจ ืžืึธืœ. ืขืจืฉื˜ืขืจ ืื™ืš ื’ืขืœืืคืŸ ื“ื™ ืงื•ื•ื™ืจื™ื– ืึธืŸ ืงื™ื™ืŸ ื™ื ื“ืขืงืกื™ื–. ื“ืึธืก ืื™ื– ื“ืึธืš ื’ืขื•ื•ืขืŸ ืึท ืžื™ื™ึทืœืข ืคื•ืŸ โ€‹โ€‹JSONB, ื•ื•ื™ื™ึทืœ ื“ื™ ื“ื–ืฉื•ื™ื ืฅ ืคืืจืœืื ื’ื˜ ืคึฟืึทืจ EAV ืงืขืŸ ื ื™ืฉื˜ ื ื•ืฆืŸ ื™ื ื“ืขืงืกื™ื– (ืคืจืขืžื“ ืฉืœื™ืกืœ ืคืขืœื“ืขืจ ื–ืขื ืขืŸ ื ื™ืฉื˜ ื™ื ื“ืขืงืกื˜). ื ืึธืš ื“ืขื, ืื™ืš ื‘ืืฉืืคืŸ ืึทืŸ ืื™ื ื“ืขืงืก ืื•ื™ืฃ ื“ื™ 2 ืคืจืขืžื“ ืฉืœื™ืกืœ ืฉืคืืœื˜ืŸ ืคื•ืŸ ื“ื™ EAV ื•ื•ืขืจื˜ ื˜ื™ืฉ, ื•ื•ื™ ื’ืขื–ื•ื ื˜ ื•ื•ื™ ืึทืŸ ืื™ื ื“ืขืงืก ื“ื–ืฉื™ืŸ ืคึฟืึทืจ ืึท JSONB ื–ื™ื™ึทืœ.

ื“ื™ ื“ืึทื˜ืŸ ื“ืขืจื”ื™ื™ึทื ื˜ื™ืงืŸ ื’ืขื•ื•ื™ื–ืŸ ื“ื™ ืคืืœื’ืขื ื“ืข ืจืขื–ื•ืœื˜ืึทื˜ืŸ ืื™ืŸ ื˜ืขืจืžื™ื ืขืŸ ืคื•ืŸ ืฆื™ื™ื˜ (ืื™ืŸ ืžื™ื–). ื‘ืึทืžืขืจืงื•ื ื’ ืึทื– ื“ื™ ื•ื•ืึธื’ ืื™ื– ืœืึธื’ืึทืจื™ื˜ื”ืžื™ืง:

ืจื™ืคึผืœื™ื™ืกื™ื ื’ EAV ืžื™ื˜ JSONB ืื™ืŸ PostgreSQL

ืžื™ืจ ื–ืขืŸ ืึทื– JSONB ืื™ื– ืคื™ืœ (> 50000-x) ืคืึทืกื˜ืขืจ ื•ื•ื™ EAV ืื•ื™ื‘ ืื™ืจ ื˜ืึธืŸ ื ื™ื˜ ื ื•ืฆืŸ ื™ื ื“ืขืงืกื™ื–, ืคึฟืึทืจ ื“ื™ ืกื™ื‘ื” ืื•ื™ื‘ืŸ. ื•ื•ืขืŸ ืžื™ืจ ืื™ื ื“ืขืงืก ืฉืคืืœื˜ืŸ ืžื™ื˜ ืขืจืฉื˜ื™ืง ืงื™ื–, ื“ื™ ื—ื™ืœื•ืง ื›ึผืžืขื˜ ืคืืจืฉื•ื•ื™ื ื“ื˜, ืึธื‘ืขืจ JSONB ืื™ื– ื ืึธืš 1,3 ืžืืœ ืคืึทืกื˜ืขืจ ื•ื•ื™ EAV. ื‘ืึทืžืขืจืงื•ื ื’ ืึทื– ื“ืขืจ ืื™ื ื“ืขืงืก ืื•ื™ืฃ ื“ื™ JSONB ื–ื™ื™ึทืœ ื”ืื˜ ืงื™ื™ืŸ ื•ื•ื™ืจืงื•ื ื’ ื“ืึธ ื–ื™ื ื˜ ืžื™ืจ ื˜ืึธืŸ ื ื™ื˜ ื ื•ืฆืŸ ื“ื™ ืคืึทืจืžืึธื’ ื–ื™ื™ึทืœ ืื™ืŸ ื“ื™ ืืคืฉืืฆื•ื ื’ ืงืจื™ื™ื˜ื™ืจื™ืึท.

ืคึฟืึทืจ ืกืึทืœืขืงื˜ื™ื ื’ ื“ืึทื˜ืŸ ื‘ืื–ื™ืจื˜ ืื•ื™ืฃ ืคืึทืจืžืึธื’ ื•ื•ืขืจื˜, ืžื™ืจ ื‘ืึทืงื•ืžืขืŸ ื“ื™ ืคืืœื’ืขื ื“ืข ืจืขื–ื•ืœื˜ืึทื˜ืŸ (ื ืึธืจืžืึทืœ ื•ื•ืึธื’):

ืจื™ืคึผืœื™ื™ืกื™ื ื’ EAV ืžื™ื˜ JSONB ืื™ืŸ PostgreSQL

ืื™ืจ ืงืขื ื˜ ื‘ืึทืžืขืจืงืŸ ืึทื– JSONB ื•ื•ื™ื“ืขืจ ืึทืจื‘ืขื˜ ืคืึทืกื˜ืขืจ ื•ื•ื™ EAV ืึธืŸ ื™ื ื“ืขืงืกื™ื–, ืึธื‘ืขืจ ื•ื•ืขืŸ EAV ืžื™ื˜ ื™ื ื“ืขืงืกื™ื–, ืขืก ื ืึธืš ืึทืจื‘ืขื˜ ืคืึทืกื˜ืขืจ ื•ื•ื™ JSONB. ืึธื‘ืขืจ ื“ืขืžืึธืœื˜ ืื™ืš ื’ืขื–ืขืŸ ืึทื– ื“ื™ ืฆื™ื™ื˜ ืคึฟืึทืจ JSONB ืงื•ื•ื™ืจื™ื– ื–ืขื ืขืŸ ื“ื™ ื–ืขืœื‘ืข, ื“ืึธืก ื”ืึธื˜ ืžื™ืจ ื’ืขืคึฟื™ืจื˜ ืฆื• ื“ื™ ืคืึทืงื˜ ืึทื– GIN ื™ื ื“ืขืงืกื™ื– ื˜ืึธืŸ ื ื™ื˜ ืึทืจื‘ืขื˜. ืžืฉืžืขื•ืช ื•ื•ืขืŸ ืื™ืจ ื ื•ืฆืŸ ืึท GIN ืื™ื ื“ืขืงืก ืื•ื™ืฃ ืึท ื–ื™ื™ึทืœ ืžื™ื˜ ืคึผืึทืคึผื™ืึทืœื™ื™ื˜ืึทื“ ืคึผืจืึธืคึผืขืจื˜ื™ืขืก, ืขืก ื ืขืžื˜ ื•ื•ื™ืจืงื•ื ื’ ื‘ืœื•ื™ื– ื•ื•ืขืŸ ืื™ืจ ื ื•ืฆืŸ ื“ื™ ืึทืจื™ื™ึทื ื ืขืžืขืŸ ืึธืคึผืขืจืึทื˜ืึธืจ @>. ืื™ืš ื’ืขื•ื•ื™ื™ื ื˜ ื“ืึธืก ืื™ืŸ ืึท ื ื™ื™ึทืข ืคึผืจืึธื‘ืข ืื•ืŸ ืขืก ื”ืื˜ ืึท ืจื™ื–ื™ืง ืคึผืจืึทืœ ืื•ื™ืฃ ื“ืขืจ ืฆื™ื™ื˜: ื‘ืœื•ื™ื– 0,153ms! ื“ืึธืก ืื™ื– 15000 ืžืืœ ืคืึทืกื˜ืขืจ ื•ื•ื™ EAV ืื•ืŸ 25000 ืžืืœ ืคืึทืกื˜ืขืจ ื•ื•ื™ ื“ื™ ->> ืึธืคึผืขืจืึทื˜ืึธืจ.

ืื™ืš ื˜ืจืึทื›ื˜ืŸ ืขืก ืื™ื– ื’ืขื•ื•ืขืŸ ืฉื ืขืœ ื’ืขื ื•ื’!

ื“ืึทื˜ืึทื‘ืึทืกืข ื˜ื™ืฉ ื’ืจื™ื™ืก

ื–ืืœ ืก ืคืึทืจื’ืœื™ื™ึทื›ืŸ ื“ื™ ื˜ื™ืฉ ืกื™ื–ืขืก ืคึฟืึทืจ ื‘ื™ื™ื“ืข ืึทืคึผืจืึธื•ื˜ืฉื™ื–. ืื™ืŸ psql ืžื™ืจ ืงืขื ืขืŸ ื•ื•ื™ื™ึทื–ืŸ ื“ื™ ื’ืจื™ื™ืก ืคื•ืŸ ืึทืœืข ื˜ื™ืฉืŸ ืื•ืŸ ื™ื ื“ืขืงืกื™ื– ื ื™ืฆืŸ ื“ื™ ื‘ืึทืคึฟืขืœ dti+

ืจื™ืคึผืœื™ื™ืกื™ื ื’ EAV ืžื™ื˜ JSONB ืื™ืŸ PostgreSQL

ืคึฟืึทืจ ื“ื™ EAV ืฆื•ื’ืึทื ื’, ื˜ื™ืฉ ืกื™ื–ืขืก ื–ืขื ืขืŸ ืึทืจื•ื 3068 ืžืขื’ืื‘ื™ื™ื˜ืŸ ืื•ืŸ ื™ื ื“ืขืงืกื™ื– ืึทืจื•ื™ืฃ ืฆื• 3427 ืžืขื’ืื‘ื™ื™ื˜ืŸ ืคึฟืึทืจ ืึท ื’ืึทื ืฅ ืคื•ืŸ 6,43 ื’ื™ื’ืื‘ื™ื™ื˜. ื“ื™ JSONB ืฆื•ื’ืึทื ื’ ื ื™ืฆื˜ 1817 MB ืคึฟืึทืจ ื“ื™ ื˜ื™ืฉ ืื•ืŸ 318 MB ืคึฟืึทืจ ื“ื™ ื™ื ื“ืขืงืกื™ื–, ื•ื•ืึธืก ืื™ื– 2,08 GB. ืขืก ื˜ื•ืจื ืก ืื•ื™ืก 3 ืžืืœ ื•ื•ื™ื™ื ื™ืงืขืจ! ื“ืขืจ ืคืึทืงื˜ ืกืึทืคึผืจื™ื™ื–ื“ ืžื™ืจ ืึท ื‘ื™ืกืœ ื•ื•ื™ื™ึทืœ ืžื™ืจ ืงืจืึธื ืคืึทืจืžืึธื’ ื ืขืžืขืŸ ืื™ืŸ ื™ืขื“ืขืจ JSONB ื›ื™ื™ืคืขืฅ.

ืึธื‘ืขืจ ื ืึธืš, ื“ื™ ื ื•ืžืขืจืŸ ืจืขื“ืŸ ืคึฟืึทืจ ื–ื™ืš: ืื™ืŸ EAV ืžื™ืจ ืงืจืึธื 2 ื™ื ื˜ืึทื“ื–ืฉืขืจ ืคืจืขืžื“ ืงื™ื– ืคึผืขืจ ืึทื˜ืจื™ื‘ื™ื•ื˜ ื•ื•ืขืจื˜, ืจื™ื–ืึทืœื˜ื™ื ื’ ืื™ืŸ 8 ื‘ื™ื˜ืขืก ืคื•ืŸ ื ืึธืš ื“ืึทื˜ืŸ. ืึทื“ื“ื™ื˜ื™ืึธื ืึทืœืœื™, EAV ืกื˜ืึธืจื– ืึทืœืข ืคืึทืจืžืึธื’ ื•ื•ืึทืœื•ืขืก ื•ื•ื™ ื˜ืขืงืกื˜, ื‘ืฉืขืช JSONB ื•ื•ืขื˜ ื ื•ืฆืŸ ื ื•ืžืขืจื™ืง ืื•ืŸ ื‘ื•ืœื™ืึทืŸ ื•ื•ืึทืœื•ืขืก ื™ื ืขืจืœืขืš ื•ื•ื• ืžืขื’ืœืขืš, ืจื™ื–ืึทืœื˜ื™ื ื’ ืื™ืŸ ืึท ืงืœืขื ืขืจืขืจ ืฉืคึผื•ืจ.

ืจืขื–ื•ืœื˜ืึทื˜ืŸ ืคื•ืŸ

ืงื•ื™ืœืขืœื“ื™ืง, ืื™ืš ื˜ืจืึทื›ื˜ืŸ ืฉืคึผืึธืจืŸ ืขื ื˜ื™ื˜ื™ ืคึผืจืึธืคึผืขืจื˜ื™ืขืก ืื™ืŸ JSONB ืคึฟืึธืจืžืึทื˜ ืงืขื ืขืŸ ืžืึทื›ืŸ ื“ื™ื–ื™ื™ื ื™ื ื’ ืื•ืŸ ืžื™ื™ื ื˜ื™ื™ื ื™ื ื’ ื“ื™ื™ืŸ ื“ืึทื˜ืึทื‘ื™ื™ืก ืคื™ืœ ื’ืจื™ื ื’ืขืจ. ืื•ื™ื‘ ืื™ืจ ืœื•ื™ืคืŸ ืึท ืคึผืœืึทืฅ ืคื•ืŸ ืคึฟืจืื’ืŸ, ื”ืึทืœื˜ืŸ ืึทืœืฅ ืื™ืŸ ื“ืขืจ ื–ืขืœื‘ื™ืงืขืจ ื˜ื™ืฉ ื•ื•ื™ ื“ื™ ืขื ื˜ื™ื˜ื™ ื•ื•ืขื˜ ืึทืงืฉืœื™ ืึทืจื‘ืขื˜ ืžืขืจ ื™ืคื™ืฉืึทื ื˜ืœื™. ืื•ืŸ ื“ืขืจ ืคืึทืงื˜ ืึทื– ื“ืึธืก ืกื™ืžืคึผืœืึทืคื™ื™ื– ื“ื™ ื™ื ื˜ืขืจืึทืงืฉืึทืŸ ืฆื•ื•ื™ืฉืŸ ื“ืึทื˜ืŸ ืื™ื– ืฉื•ื™ืŸ ืึท ืคึผืœื•ืก, ืึธื‘ืขืจ ื“ื™ ืจื™ื–ืึทืœื˜ื™ื ื’ ื“ืึทื˜ืึทื‘ื™ื™ืก ืื™ื– 3 ืžืืœ ืงืœืขื ืขืจืขืจ ืื™ืŸ ื‘ืึทื ื“.

ืื•ื™ืš, ื‘ืื–ื™ืจื˜ ืื•ื™ืฃ ื“ื™ ื“ื•ืจื›ื’ืขืงืึธื›ื˜ ื˜ืขืกืฅ, ืžื™ืจ ืงืขื ืขืŸ ืคืึทืจืขื ื“ื™ืงืŸ ืึทื– ื“ื™ ืคืึธืจืฉื˜ืขืœื•ื ื’ ืœืึธืกืกืขืก ื–ืขื ืขืŸ ื–ื™ื™ืขืจ ื ื™ืฉื˜ื™ืง. ืื™ืŸ ืขื˜ืœืขื›ืข ืงืึทืกืขืก, JSONB ืื™ื– ืืคื™ืœื• ืคืึทืกื˜ืขืจ ื•ื•ื™ EAV, ื•ื•ืึธืก ืžืื›ื˜ ืขืก ืืคื™ืœื• ื‘ืขืกืขืจ. ืึธื‘ืขืจ, ื“ืขืจ ื‘ืขื ื˜ืฉืžืึทืจืง ืคื•ืŸ ืงื•ืจืก ื˜ื•ื˜ ื ื™ืฉื˜ ื“ืขืงืŸ ืึทืœืข ืึทืกืคึผืขืงืฅ (ืœืžืฉืœ ืขื ื˜ื™ื˜ื™ื– ืžื™ื˜ ืึท ื–ื™ื™ืขืจ ื’ืจื•ื™ืก ื ื•ืžืขืจ ืคื•ืŸ ืคึผืจืึธืคึผืขืจื˜ื™ืขืก, ืึท ื‘ืึทื˜ื™ื™ื˜ื™ืง ืคืึทืจื’ืจืขืกืขืจืŸ ืื™ืŸ ื“ื™ ื ื•ืžืขืจ ืคื•ืŸ ืคึผืจืึธืคึผืขืจื˜ื™ืขืก ืคื•ืŸ ื™ื’ื–ื™ืกื˜ื™ื ื’ ื“ืึทื˜ืŸ, ...), ืึทื–ื•ื™ ืื•ื™ื‘ ืื™ืจ ื”ืึธื‘ืŸ ืงื™ื™ืŸ ืคึฟื™ืจืœื™ื™ื’ืŸ ืื•ื™ืฃ ื•ื•ื™ ืฆื• ืคึฟืึทืจื‘ืขืกืขืจืŸ ื–ื™ื™ , ื‘ื™ื˜ืข ืคื™ืœืŸ ืคืจื™ื™ ืฆื• ืœืึธื–ืŸ ืื™ืŸ ื“ื™ ื‘ืึทืžืขืจืงื•ื ื’ืขืŸ!

ืžืงื•ืจ: www.habr.com

ืœื™ื™ื’ืŸ ืึท ื‘ืึทืžืขืจืงื•ื ื’