Wzorce i strategie NoSQL
Twój schemat MongoDB wyglądał czysto na demie: jeden gruby dokument user z osadzonymi zamówieniami, adresami i koszykiem. Sześć miesięcy później ta tablica ma 4000 wpisów, ocierasz się o limit 16 MB na dokument, a każdy odczyt ciągnie całość przez sieć. Agent, który wygenerował ten schemat, nigdy nie widział twoich wzorców dostępu ani stosunku odczytów do zapisów — więc zoptymalizował go pod demo, a nie pod produkcję.
W NoSQL nie ma migracji schematu, za którymi można się schować. Model danych jest projektem, a zła decyzja „osadzić czy referencjonować” jest kosztowna do odkręcenia, gdy masz już ruch. Dobra wiadomość: agenci kodujący AI radzą sobie z tym świetnie, kiedy z góry dasz im ograniczenia i pozwolisz weryfikować na żywej bazie przez serwer MCP.
Co z tego wyniesiesz
Dział zatytułowany „Co z tego wyniesiesz”- Prompt decyzyjny, który zmusza agenta, by uzasadnił osadzenie vs. referencję na podstawie twoich faktycznych wzorców dostępu i stosunku odczytów do zapisów
- Prompt do potoku agregacji, który wymaga planu
explain()i indeksu pokrywającego, a nie tylko zapytania, które „działa” - Prompt do projektu jednej tabeli DynamoDB, który wymienia dokładne wzorce dostępu i prosi o projekcje GSI
- Stan „przed/po” podłączeniu serwerów MCP MongoDB i Neo4j, by agent introspektował twój prawdziwy schemat zamiast zgadywać
- Gotowe do uruchomienia fragmenty AWS SDK v3 i Neo4j 5, które dziś wkleisz do realnego projektu
Przepływ pracy
Dział zatytułowany „Przepływ pracy”Wzorzec jest ten sam dla każdego magazynu: daj agentowi wzorce dostępu i ograniczenie, każ mu wytworzyć projekt jako artefakt, a potem niech zweryfikuje go na żywej instancji przez MCP. Bez MCP agent halucynuje nazwy kolekcji i kształty indeksów; z MCP agent czyta twój prawdziwy schemat i dowodzi swoich zapytań.
Krok 1 — Podłącz serwer MCP bazy danych
Dział zatytułowany „Krok 1 — Podłącz serwer MCP bazy danych”To największe pojedyncze usprawnienie pracy z NoSQL przy udziale agenta. Serwer MCP MongoDB (mongodb-mcp-server, na npm) pozwala agentowi listować kolekcje, próbkować dokumenty, uruchamiać explain() i tworzyć indeksy na twoim prawdziwym klastrze. Serwer MCP Neo4j Cypher (mcp-neo4j-cypher, w Pythonie — uruchamiany przez uvx) robi to samo dla grafu. Konfiguracja MCP jest identyczna we wszystkich trzech narzędziach poza tym, gdzie żyje konfiguracja.
Dodaj do .cursor/mcp.json (projekt) lub ~/.cursor/mcp.json (globalnie), a następnie włącz serwer w Settings → MCP:
{ "mcpServers": { "mongodb": { "command": "npx", "args": ["-y", "mongodb-mcp-server", "--connectionString", "mongodb://localhost:27017/shop"] } }}Zarejestruj go z terminala — serwer stdio oparty na npx:
claude mcp add mongodb -- npx -y mongodb-mcp-server \ --connectionString "mongodb://localhost:27017/shop"Serwer Neo4j jest w Pythonie, więc jako polecenia użyj uvx:
claude mcp add neo4j -- uvx mcp-neo4j-cypher \ --db-url bolt://localhost:7687 --username neo4j --password "$NEO4J_PASSWORD"Dodaj do ~/.codex/config.toml w tabeli [mcp_servers.*]:
[mcp_servers.mongodb]command = "npx"args = ["-y", "mongodb-mcp-server", "--connectionString", "mongodb://localhost:27017/shop"]Krok 2 — Uczyń decyzję „osadzić vs. referencjonować” jawną
Dział zatytułowany „Krok 2 — Uczyń decyzję „osadzić vs. referencjonować” jawną”Najbardziej brzemienną w skutki decyzją w magazynie dokumentów jest to, czy powiązane dane są osadzone, czy referencjonowane. Agent trafia z tym za pierwszym razem, gdy wręczysz mu wzorce dostępu i stosunek odczytów do zapisów, zamiast prosić go o „zaprojektowanie dobrego schematu”.
Dobra odpowiedź osadza pozycje zamówienia (ograniczone, niezmienne, zawsze czytane razem), przechowuje zdenormalizowaną migawkę customer: { id, name } zamiast robić $lookup przy każdym odczycie i trzyma osobną kolekcję orderItems tylko wtedy, gdy pozycje mogą rosnąć bez ograniczeń. Wytworzony projekt to artefakt, który recenzujesz — agent powinien wytworzyć go jako opisany schemat, a nie zakopać w prozie.
Krok 3 — Generuj potoki agregacji, które same się dowodzą
Dział zatytułowany „Krok 3 — Generuj potoki agregacji, które same się dowodzą”Potoki agregacji to miejsce, w którym AI oszczędza najwięcej czasu, ale też najczęściej tworzy zapytanie zwracające poprawne wyniki przy pełnym skanie kolekcji. Lekarstwem jest wymaganie planu wykonania jako części dostarczanego rezultatu.
Z podłączonym serwerem MCP MongoDB agent może faktycznie uruchomić to explain() i iterować, aż etap trafi w IXSCAN — domykając pętlę, zamiast zostawiać tobie odkrycie pełnego skanu na produkcji.
Ta sama pętla obejmuje pracę w czasie rzeczywistym. Strumień zmian MongoDB to kilka linijek i jest identyczny we wszystkich trzech narzędziach — wartością jest prompt, a nie szablonowy kod:
import { MongoClient } from 'mongodb';
const client = new MongoClient(process.env.MONGODB_URI);await client.connect();const products = client.db('shop').collection('products');
// Tokeny wznowienia przetrwają restart; na produkcji przechowuj ostatni trwale.const changeStream = products.watch( [{ $match: { 'fullDocument.inventory.available': { $lt: 10 } } }], { fullDocument: 'updateLookup' });
for await (const change of changeStream) { await notifyLowStock(change.fullDocument);}Krok 4 — Projekt jednej tabeli DynamoDB
Dział zatytułowany „Krok 4 — Projekt jednej tabeli DynamoDB”DynamoDB najsurowiej karze za niejasne prompty: pomyl klucz partycji, a stworzysz gorące partycje, które dławią się pod obciążeniem. Wymień z góry każdy wzorzec dostępu i każ agentowi zmapować każdy z nich na warunek klucza.
Powstałą mapę kluczy i tabelę GSI traktuj jako artefakt projektowy. Część wykonywalna jest niewielka — i musi używać AWS SDK v3, bo v2 jest po zakończeniu wsparcia. Oto prawdziwe, gotowe do uruchomienia zapytanie pod wzorzec dostępu getUserTasks:
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';import { DynamoDBDocumentClient, QueryCommand } from '@aws-sdk/lib-dynamodb';
const ddb = DynamoDBDocumentClient.from(new DynamoDBClient({}));
// GSI1: PK = USER#<id>, SK = TASK#<dueDate>#<taskId>export async function getUserTasks(userId, fromDate) { const { Items } = await ddb.send( new QueryCommand({ TableName: 'SaaSPlatform', IndexName: 'GSI1', KeyConditionExpression: 'GSI1PK = :pk AND GSI1SK >= :start', ExpressionAttributeValues: { ':pk': `USER#${userId}`, ':start': `TASK#${fromDate}`, }, ScanIndexForward: true, // rosnąco według terminu }) ); return Items;}Zwróć uwagę na await ddb.send(new QueryCommand(...)) — łańcuch .query(params).promise() z v2 już nie obowiązuje. Zapisy wsadowe mają ten sam kształt z BatchWriteCommand (wciąż ograniczone do 25 pozycji na żądanie).
Krok 5 — Modelowanie grafu z Neo4j
Dział zatytułowany „Krok 5 — Modelowanie grafu z Neo4j”Dla danych silnie powiązanych (grafy społecznościowe, rekomendacje, siatki oszustw) graf bije złączenia. Poproś agenta najpierw o model węzłów/relacji, a potem o Cypher — i upewnij się, że emituje składnię Neo4j 5, bo dane treningowe agenta są pełne usuniętej formy ograniczeń z v4.
Poprawne ograniczenia Neo4j 5 i zapytanie rekomendacyjne wyglądają tak:
CREATE CONSTRAINT user_id IF NOT EXISTSFOR (u:User) REQUIRE u.id IS UNIQUE;
CREATE CONSTRAINT post_id IF NOT EXISTSFOR (p:Post) REQUIRE p.id IS UNIQUE;
// Posty, które znajomi polubili w ciągu ostatnich 7 dni, a których ten użytkownik nie obejrzałMATCH (user:User {id: $userId})-[:FRIENDS]-(friend:User)MATCH (friend)-[:LIKED]->(post:Post)WHERE NOT (user)-[:VIEWED]->(post) AND post.createdAt > datetime() - duration('P7D')WITH post, count(DISTINCT friend) AS friendLikesRETURN post.id, post.content, friendLikesORDER BY friendLikes DESCLIMIT 10;Z podłączonym serwerem MCP Neo4j agent uruchamia każde zapytanie przed oddaniem go, wyłapując literówki w etykietach i naleciałości z v4, które inaczej wysadziłyby się w czasie działania.
Krok 6 — Wyszukiwanie wektorowe
Dział zatytułowany „Krok 6 — Wyszukiwanie wektorowe”Przechowywanie wektorów osadzeń obok dokumentów pozwala robić wyszukiwanie semantyczne bez osobnej bazy wektorowej. W MongoDB Atlas nowoczesnym API jest typ indeksu vectorSearch z tablicą fields i numDimensions — starsze mapowanie knnVector jest przestarzałe i niekompatybilne z etapem agregacji $vectorSearch, którym faktycznie odpytujesz.
from pymongo.operations import SearchIndexModel
# Indeks vectorSearch wymaga SearchIndexModel — forma create_search_index()# z czystym słownikiem tworzy tylko indeksy Atlas Search i ignoruje `type`.search_index_model = SearchIndexModel( definition={ "fields": [ { "type": "vector", "path": "text_embedding", "numDimensions": 384, # musi dokładnie odpowiadać twojemu modelowi osadzeń "similarity": "cosine", }, {"type": "filter", "path": "price"}, {"type": "filter", "path": "categories"}, ] }, name="product_embeddings", type="vectorSearch",)db.products.create_search_index(model=search_index_model)
# Odpytuj go etapem $vectorSearch, wstępnie filtrując po zaindeksowanych polach filtrapipeline = [ { "$vectorSearch": { "index": "product_embeddings", "path": "text_embedding", "queryVector": query_embedding, # 384-wymiarowa lista z twojego modelu "numCandidates": 200, "limit": 10, "filter": {"price": {"$lte": 100}}, } }, {"$addFields": {"score": {"$meta": "vectorSearchScore"}}}, {"$project": {"text_embedding": 0}},]Kiedy to się sypie
Dział zatytułowany „Kiedy to się sypie”- Niekontrolowany wzrost tablicy / limit 16 MB. Osadzanie
events,commentsczylineItems, które rosną w nieskończoność, w końcu uderza w limit 16 MB na dokument w MongoDB i spowalnia każdy odczyt. Referencjonuj do kolekcji potomnej, gdy tylko tablica może rosnąć bez ograniczeń; poproś agenta, by oznaczył to na etapie projektowania schematu. - Gorące partycje DynamoDB. Klucz partycji o niskiej kardynalności (np.
TENANT#<id>dla kilku ogromnych najemców) dławi się pod obciążeniem niezależnie od zaaprowizowanej pojemności. Dodaj sufiks shardujący zapisy (TENANT#<id>#<shard>) i rób scatter-gather przy odczycie. - Superwęzły Neo4j. Celebryta z milionami krawędzi
FRIENDSzamienia przejścia po grafie w skany tabeli. Ogranicz rozejście (fan-out) w zapytaniach lub zamodeluj gorącą relację jako osobny węzeł. - Nieaktualne wektory osadzeń. Wyniki wektorowe po cichu degradują się, gdy zmieniasz model osadzeń, ale nie reindeksujesz istniejących dokumentów, lub gdy
numDimensionsprzestaje pasować do modelu. Przeliczaj osadzenia przy zmianie modelu i asercjuj liczbę wymiarów. - Agent wymyśla schemat. Bez połączenia MCP zgaduje nazwy kolekcji i pól. Jeśli zapytania odwołują się do pól, których nie masz, zatrzymaj się i podłącz serwer MCP MongoDB lub Neo4j, by czytał prawdziwy schemat.
Co dalej
Dział zatytułowany „Co dalej”- Wzorce i strategie SQL — kiedy model relacyjny jest właściwym wyborem i jak o niego promptować
- Wzorce ORM — bezpieczny typowo dostęp do danych nad tymi magazynami z Prismą, Drizzle i Mongoose
- Wzorce migracji — przenoszenie między magazynami i bezpieczna ewolucja modeli NoSQL