Anagram Server est une application basée sur Node.js qui expose une API basée sur REST pour effectuer des recherches liées à l'anagramme par rapport à un dictionnaire de mots. Sa principale caractéristique est de trouver des anagrammes connus pour un mot donné.
De plus, les ensembles d'anagramme (groupes de mots qui sont des anagrammes les uns des autres) peuvent être interrogés par cardinalité (nombre de mots dans l'ensemble) ou la longueur des mots. Il est également possible de demander si un ensemble donné de mots comprend un ensemble d'anagramme.
Le dictionnaire de mots contre lequel les anagrammes peuvent être interrogés peuvent être ajoutés, supprimés ou entièrement effacés via l'API. Lorsqu'il est configuré comme un service à la mémoire uniquement (c'est-à-dire, les modifications ne sont pas persistées dans les redémarrages du service), Anagram Server précharge un ensemble standard de mots anglais au démarrage (voir app.js
).
Enfin, un certain nombre de statistiques sur le dictionnaire chargé peuvent être interrogées via l'API.
Installez Node.js si nécessaire
Installer les dépendances NPM
npm install
npm start
Par défaut, l'application sert des demandes sur le port 3000. Pour remplacer cela, vous pouvez mettre à jour le script de démarrage dans package.json
pour passer un autre numéro de port à la commande Node. Par exemple:
"start": "node src/app.js -p 8080"
Vous devrez peut-être explicitement autoriser le trafic entrant sur le port effectif. Par exemple, pour ouvrir le port 3000 sur Linux jusqu'au prochain redémarrage:
sudo iptables -A INPUT -m state --state NEW -m tcp -p tcp --dport 3000 -j ACCEPT
Installez Docker si nécessaire
Construisez l'image Docker
sudo docker build -t anagram-server .
sudo docker run -p 3000:3000 anagram-server
Vous préférez peut-être mapper à un port hôte alternatif (par exemple, -p 8080:3000
).
Le serveur Anagram est expédié avec des scripts de test Ruby.
Notez que par défaut, le serveur Anagram précharge les mots de dictionary.txt
au démarrage.
Les scripts de test sont dans le sous-dossier test
du package source et peuvent être exécutés individuellement comme:
ruby anagram_test.rb
ruby anagram_test_2.rb
Anagram Server peut être testé manuellement avec cURL
ou un outil comme Postman. Par exemple (à partir de la ligne de commande dans une nouvelle fenêtre de terminal de l'hôte de l'application):
curl -i "http://localhost:3000/anagrams/shout.json"
Étant donné que par défaut, le serveur Anagram précharge les mots de dictionary.txt
au démarrage, vous pouvez effacer le dictionnaire avec la commande suivante avant le test:
curl -i -X DELETE "http://localhost:3000/words.json"
Pour les tests distants, remplacez "LocalHost" par l'App Host IP dans anagram_client.rb
et dans les exemples de commandes de ce document.
Mettez également à jour le numéro de port dans anagram_client.rb
et dans les exemples de commandes si l'exécution du serveur Anagram avec un port autre que la valeur par défaut (3000).
Un mot est considéré comme valide s'il contient une combinaison de lettres d'alphabet anglaise en majuscules et en minuscules ou un trait d'union. Un mot valide peut ne pas commencer ni terminer par un trait d'union.
Les tentatives pour obtenir ou supprimer des mots non valides entraînent 400 Bad Request
.
Les tentatives de publication de mots invalides entraînent 204 No Content
.
Un nom approprié est considéré comme un mot qui a toutes les lettres minuscules à l'exception de la première lettre (qui doit être majuscule) et la première lettre après un trait d'union (qui peut être majuscule ou minuscule).
Certains exemples sont: l'anglais, le zoulu, le jean-christophe
Les noms appropriés sont considérés comme distincts de leurs versions minuscules. Par exemple, Abigail et Abigail sont deux mots distincts (et anagrammes l'un de l'autre).
Les noms appropriés sont toujours inclus dans les résultats, sauf exclure explicitement (voir excludeProperNouns
parm of GET /anagrams/:word.json
).
Pour plus de commodité, Anagram Server permet de faire correspondre les noms appropriés contre leurs versions minuscules dans certains cas. Par exemple, lorsque vous interrogez les anagrammes:
$ curl -i "http://localhost:3000/anagrams/aaru.json?includeInput=true"
HTTP/1.1 200 OK
Content-Type: application/json
...
{ "anagrams": [
"Aaru",
"aura"]
}
L'architecture du serveur Anagram se compose de 4 couches (du plus bas niveau au plus haut):
Un adaptateur est une classe qui fournit les opérations de requête, d'itération et de crud spécifiques au magasin de base utilisées par Anagram Server. Plus précisément, un adaptateur fournit une sémantique pour associer une chaîne de clé à un ensemble de valeurs, en ajoutant et en supprimant de l'ensemble de valeurs par clé, en interrogeant pour un ensemble par clé et en itérant des paires de touches / set.
L'adaptateur résume les spécificités du mécanisme de stockage sous-jacent de la logique de service afin de faciliter l'échange d'une technologie de stockage contre une autre. Une valeur de cette abstraction consiste à fournir un chemin de mise à niveau facile à mesure que des alternatives de stockage plus favorables émergent et de permettre des options d'évolutivité flexibles.
Par exemple, le service peut initialement être déployé en tant que serveur d'applications unique avec un adaptateur qui enveloppe une instance MySQL sur le même serveur. À mesure que l'évolutivité, le basculement et les besoins de performances augmentent, nous pouvons échanger l'adaptateur contre celui qui enveloppe une instance redis qui persiste et reproduit ses données sur plusieurs serveurs. Les spécificités de la façon dont les données sont stockées, mises en cache et / ou reproduites sont transparentes au service Anagram.
Anagram Server est expédié avec MemoryAdapter ( adapters/MemoryAdapter.js
), qui utilise la carte de JavaScript pour stocker et interroger les données. Cet adaptateur a une application limitée car elle ne profite pas de la persistance entre les redémarrages du serveur, mais il sert de bonne base pour tester et afficher les fonctionnalités du serveur anagram.
Le projet définit une interface pour implémenter les adaptateurs dans les adapters/adapter-template.js
. Ce fichier peut être utilisé comme passe-partout pour définir de nouveaux adaptateurs.
L'interface de l'adaptateur est basée sur les promesses car les API pour les technologies de stockage ont tendance à être asynchrones. Théoriquement, cela ajoute le temps de réponse depuis que les promesses sont résolues via la file d'attente d'événements, mais cet effet est négligeable dans le cadre d'une demande de réseau.
Transactions
Les méthodes add()
et delete()
de l'adaptateur nécessitent que la boutique sous-jacente prenne en charge les transactions car leur logique implique l'interrogation des données, puis fonctionnant sur le magasin en fonction des résultats de la requête.
Résultats de clonage
MemoryAdapter get()
et each()
méthodes renvoient les tableaux de valeur de carte directement dans l'anagramservice. Cela nécessite une diligence au nom du code d'anagramservice pour éviter une mutation accidentelle des résultats fournis par ces méthodes.
Le clonage des résultats au sein de la mémoire avant de les retourner serait une étape sage pour atténuer les futurs bogues, assurer la cohérence de l'interface et offrir le moins d'étonnement aux consommateurs, mais implique également des frais généraux supplémentaires (quoique probablement négligeables).
AnagramService est une classe qui fournit la logique métier pour le serveur Anagram. Il nécessite une instance d'un adaptateur à transmettre à son constructeur.
La classe AnagramService maintient les comptes de mots et d'anagramme et implémente des méthodes qui soutiennent directement l'API REST.
Cette classe vit dans AnagramService.js
.
server.js
exporte une seule fonction startServer()
qui crée le serveur de repos (via restify) et instancie anagramservice.
startServer()
nécessite une instance d'adaptateur et accepte éventuellement un numéro de port à partir desquels des demandes de service et un chemin facultatif vers un fichier texte pour préparer le dictionnaire.
La viande de server.js
est l'ensemble des fonctions de réponse du serveur qui analysent les demandes HTTP individuelles, appellent les méthodes anagramservices pertinentes et publient des réponses avec un emballage d'objet approprié et des codes de réponse HTTP.
app.js
est le point d'entrée pour le serveur Anagram. Il s'agit d'un fichier simple qui spécifie l'adaptateur pour exécuter le service avec une source de précharge de données facultative.
Il s'agit du seul fichier qui doit changer lors de l'échange d'un adaptateur pour un autre.
La version actuelle d' app.js
exécute AnagramServer avec le MemoryAdapter et Preloads dictionary.txt
sur le démarrage.
Vous trouverez ci-dessous quelques idées pour développer davantage le serveur Anagram.
GET /anagrams/:word.json
Renvoyez un tableau JSON de mots qui sont des anagrammes du mot passé dans l'URL.
Si le mot passé lui-même n'est pas un mot connu (c'est-à-dire pas dans le dictionnaire), un tableau vide est retourné (même si des anagrammes connus peuvent être formés à partir du mot passé).
Pour plus de commodité, un mot passé car les minuscules correspondront à sa forme de nom appropriée.
Exemple:
$ curl -i "http://localhost:3000/anagrams/care.json"
HTTP/1.1 200 OK
Content-Type: application/json
...
{ "anagrams": [
"Acer",
"acre",
"crea",
"race"]
}
GET /anagrams/:word.json?limit=<integer>
Renvoyez un tableau JSON de mots qui sont des anagrammes du mot passé dans l'URL, mais limitez le nombre de résultats renvoyés .
Exemple:
$ curl -i "http://localhost:3000/anagrams/care.json?limit=2"
HTTP/1.1 200 OK
Content-Type: application/json
...
{ "anagrams": [
"Acer",
"acre"]
}
GET /anagrams/:word.json?includeInput=true
Renvoyez un tableau JSON de mots qui sont des anagrammes du mot passé dans l'URL, y compris le mot d'entrée lui-même .
Le mot d'entrée n'est normalement pas inclus dans les résultats de l'anagramme car un mot n'est pas conventionnellement considéré comme une anagramme de lui-même.
$ curl -i "http://localhost:3000/anagrams/care.json?includeInput=true"
HTTP/1.1 200 OK
Content-Type: application/json
...
{ "anagrams": [
"Acer",
"acre",
"care",
"crea",
"race"]
}
GET /anagrams/:word.json?excludeProperNouns=true
Renvoyez un tableau JSON de mots qui sont des anagrammes du mot passé dans l'URL, omettant des noms appropriés .
Les noms appropriés sont normalement inclus dans les résultats de l'anagramme.
$ curl -i "http://localhost:3000/anagrams/care.json?limit=2&excludeProperNouns=true"
HTTP/1.1 200 OK
Content-Type: application/json
...
{ "anagrams": [
"acre",
"crea"]
}
GET /anagrams?cardinalityMin=<integer>&cardinalityMax=<integer>
Renvoie tous les ensembles anagrammes qui ont une cardinalité minimale et / ou maximale (nombre d'anagrammes dans l'ensemble).
CardinalityMin ou CardinalityMax peuvent être omis.
Exemples:
$ curl -i "http://localhost:3000/anagrams?cardinalityMin=3&cardinalityMax=4"
HTTP/1.1 200 OK
Content-Type: application/json
...
{
"anagramsByCardinality": {
"cardinalityMin": 3,
"cardinalityMax": 4,
"anagrams": [
["Aaronic", "Nicarao", "ocarina"],
["abater", "artabe", "eartab", "trabea"],
["Abe", "bae", "Bea"],
...
]
}
}
# Return all words that have anagrams
$ curl -i "http://localhost:3000/anagrams?cardinalityMin=2"
HTTP/1.1 200 OK
Content-Type: application/json
...
{
"anagramsByCardinality": {
"cardinalityMin": 2,
"anagrams": [
["A", "a"],
["aal", "ala"],
["aam", "ama"],
...
]
}
}
GET /anagrams?lengthMin=<integer>&lengthMax=<integer>
Renvoie tous les ensembles anagrammes qui ont une longueur de mot minimum et / ou maximale.
La longueur ou la longueur de longueur peuvent être omises.
Exemple:
$ curl -i "http://localhost:3000/anagrams?lengthMin=10&lengthMax=11"
HTTP/1.1 200 OK
Content-Type: application/json
...
{
"anagramsByLength": {
"lengthMin": 10,
"lengthMax": 11,
"anagrams": [
["ablastemic", "masticable"],
["aborticide", "bacterioid"],
["acalyptrate", "Calyptratae"],
...
]
}
}
GET /anagrams?maxCardinality=true
Renvoyez tous les ensembles anagrammes avec la cardinalité maximale.
Exemple:
$ curl -i "http://localhost:3000/anagrams?maxCardinality=true"
HTTP/1.1 200 OK
Content-Type: application/json
...
{
"maxCardinalityAnagrams": {
"maxCardinality": 11,
"anagrams": [
["angor", "argon", "goran", "grano", "groan", "nagor", "Orang", "orang", "organ", "rogan", "Ronga"]
]
}
}
GET /anagrams?maxLength=true
Renvoie tous les ensembles anagrammes avec la longueur de mot maximale.
Exemple:
$ curl -i "http://localhost:3000/anagrams?maxLength=true"
HTTP/1.1 200 OK
Content-Type: application/json
...
{
"maxLengthAnagrams": {
"maxLength": 22,
"anagrams": [
["cholecystoduodenostomy", "duodenocholecystostomy"],
["hydropneumopericardium", "pneumohydropericardium"]
]
}
}
GET /anagrams?areAnagrams=<comma-delimited list of words>
Déterminez si un ensemble de mots est des anagrammes les uns des autres.
Tous les mots passés doivent être connus (c'est-à-dire dans le dictionnaire) pour que cela soit vrai.
Exemple:
$ curl -i "http://localhost:3000/anagrams?areAnagrams=acer,acre,race"
HTTP/1.1 200 OK
Content-Type: application/json
...
{
"anagramAffinity": {
"areAnagrams": true,
"words": ["acer", "acre", "race"]
}
}
GET /anagrams?count=true
Retourner Anagram compte uniquement. Chaque ensemble d'anagrammes du dictionnaire ajoute N-1 à ce décompte, où n est le nombre d'anagrammes dans l'ensemble.
Exemple:
$ curl -i "http://localhost:3000/anagrams?count=true"
HTTP/1.1 200 OK
Content-Type: application/json
...
{ "counts": { "anagram": 20043 }}
GET /words?count=true
Retour Nombre de mots dans le dictionnaire.
Exemple:
$ curl -i "http://localhost:3000/words?count=true"
HTTP/1.1 200 OK
Content-Type: application/json
...
{ "counts": { "word": 235886 }}
GET /words?stats=true
Renvoyez des statistiques sur les mots du dictionnaire.
Exemple:
$ curl -i "http://localhost:3000/words?stats=true"
HTTP/1.1 200 OK
Content-Type: application/json
...
{
"stats": {
"wordCount": 235886,
"anagramCount": 20043,
"minWordLength": 1,
"maxWordLength": 24,
"medianWordLength": 4,
"averageWordLength": 9.569126612007494,
"minCardinality": 2,
"maxCardinality": 11,
"medianCardinality": 2,
"averageCardinality": 2.3111140184470464
}
}
POST /words.json
Prend une gamme JSON de mots et les ajoute au dictionnaire.
Exemple:
$ curl -i -X POST -d '{ "words": ["Canadas", "acandas", "Smurfs", "care"] }' "http://localhost:3000/words.json"
HTTP/1.1 201 Created
Content-Type: application/json
...
{
"counts": {
"word": 3,
"anagram": 1
},
"words": ["/anagrams/Canadas", "/anagrams/acandas", "/anagrams/Smurfs"]
}
DELETE /words/:word.json
Supprimez un seul mot du dictionnaire.
Si le mot passé lui-même n'est pas un mot connu (c'est-à-dire pas dans le dictionnaire), un 404
est retourné.
Exemple:
$ curl -i -X DELETE "http://localhost:3000/words/care.json"
HTTP/1.1 204 No Content
...
DELETE /words/:word.json?includeAnagrams=true
Supprimez un seul mot et toutes ses anagrammes du dictionnaire.
Si le mot passé lui-même n'est pas un mot connu (c'est-à-dire pas dans le dictionnaire), rien n'est supprimé et un 404
est retourné.
Exemple:
$ curl -i -X DELETE "http://localhost:3000/words/acre.json?includeAnagrams=true"
HTTP/1.1 204 No Content
...
DELETE /words.json
Effacer tous les contenus du dictionnaire.
Exemple:
$ curl -i -X DELETE "http://localhost:3000/words.json"
HTTP/1.1 204 No Content
...