Anagram Server es una aplicación basada en Node.js que expone una API basada en REST para realizar búsquedas relacionadas con el anagrama contra un diccionario de palabras. Su característica principal es encontrar anagramas conocidos para una palabra dada.
Además, los conjuntos de anagramas (grupos de palabras que son anagramas entre sí) se pueden consultar por cardinalidad (número de palabras en el conjunto) o longitud de las palabras. También es posible consultar si un conjunto de palabras determinado comprende un conjunto de anagramas.
El diccionario de palabras contra los cuales se pueden consultar anagramas se puede agregar, eliminar o eliminarse por completo a través de la API. Cuando se configura como un servicio solo de memoria (es decir, los cambios que no se persisten en el servicio se reinician), el servidor de anagram prevalece un conjunto estándar de palabras en inglés al inicio (ver app.js
).
Finalmente, se pueden consultar varias estadísticas sobre el diccionario cargado a través de la API.
Instale Node.js si es necesario
Instalar dependencias de NPM
npm install
npm start
De manera predeterminada, la aplicación sirve solicitudes sobre el puerto 3000. Para anular esto, puede actualizar el script de inicio en package.json
para pasar un número de puerto alternativo al comando nodo. Por ejemplo:
"start": "node src/app.js -p 8080"
Es posible que deba permitir explícitamente el tráfico entrante en el puerto efectivo. Por ejemplo, para abrir el puerto 3000 en Linux hasta el próximo reinicio:
sudo iptables -A INPUT -m state --state NEW -m tcp -p tcp --dport 3000 -j ACCEPT
Instale Docker si es necesario
Construye la imagen Docker
sudo docker build -t anagram-server .
sudo docker run -p 3000:3000 anagram-server
Puede preferir asignar un puerto de host alternativo (por ejemplo, -p 8080:3000
).
El servidor de anagram se envía con scripts de prueba de Ruby.
Tenga en cuenta que, de forma predeterminada, el servidor de anagram precarga las palabras de dictionary.txt
al inicio.
Los scripts de prueba están en la subcarpeta test
del paquete fuente y se pueden ejecutar individualmente como:
ruby anagram_test.rb
ruby anagram_test_2.rb
El servidor de anagram se puede probar manualmente con cURL
o una herramienta como Postman. Por ejemplo (desde la línea de comando en una nueva ventana de terminal del host de la aplicación):
curl -i "http://localhost:3000/anagrams/shout.json"
Dado que, de forma predeterminada, el servidor de anagram precarga las palabras de dictionary.txt
al inicio, es posible que desee borrar el diccionario con el siguiente comando antes de las pruebas:
curl -i -X DELETE "http://localhost:3000/words.json"
Para pruebas remotas, reemplace "localhost" con la IP del host de la aplicación en anagram_client.rb
y en los comandos de muestra de este documento.
También actualice el número de puerto en anagram_client.rb
y en los comandos de muestra si se ejecuta un servidor de anagram con un puerto que no sea el predeterminado (3000).
Una palabra se considera válida si contiene alguna combinación de letras de alfabeto en inglés y mayúsculas y un guión. Una palabra válida no puede comenzar o terminar con un guión.
Los intentos de obtener o eliminar palabras no válidas dan como resultado 400 Bad Request
.
Los intentos de publicar palabras inválidas dan como resultado 204 No Content
.
Se considera que un sustantivo propio es cualquier palabra que tenga todas las letras minúsculas, excepto la primera letra (que debe estar en mayúsculas) y la primera letra después de un guión (que puede ser en mayúsculas o minúsculas).
Algunos ejemplos son: inglés, zulu, jean-christophe
Los sustantivos propios se consideran distintos de sus versiones en minúsculas. Por ejemplo, Abigail y Abigail son dos palabras distintas (y anagramas entre sí).
Los sustantivos propios siempre se incluyen en los resultados a menos que se excluyan explícitamente (ver excludeProperNouns
Parm de GET /anagrams/:word.json
).
Por conveniencia, el servidor de anagram permite hacer coincidir los sustantivos propios con sus versiones en minúsculas en algunos casos. Por ejemplo, al consultar anagramas:
$ curl -i "http://localhost:3000/anagrams/aaru.json?includeInput=true"
HTTP/1.1 200 OK
Content-Type: application/json
...
{ "anagrams": [
"Aaru",
"aura"]
}
La arquitectura del servidor de anagrama consta de 4 capas (desde el nivel más bajo hasta el más alto):
Un adaptador es una clase que proporciona las operaciones básicas de consultas, iteraciones y CRUD específicas de la tienda utilizadas por Anagram Server. Específicamente, un adaptador proporciona semántica para asociar una cadena clave a un conjunto de valores, agregando y eliminando del conjunto de valores por clave, consultando un conjunto por tecla e iterando pares de clave/conjunto.
El adaptador abstrae los detalles del mecanismo de almacenamiento subyacente de la lógica de servicio para facilitar el intercambio de una tecnología de almacenamiento por otra. Un valor de esta abstracción es proporcionar una ruta de actualización fácil a medida que surjan alternativas de almacenamiento más favorables y permitir opciones de escalabilidad flexible.
Por ejemplo, el servicio puede implementarse inicialmente como un único servidor de aplicaciones con un adaptador que envuelve una instancia MySQL en el mismo servidor. A medida que aumentan la escalabilidad, la conmutación por error y el rendimiento, podríamos intercambiar el adaptador por uno que envuelva una instancia de Redis que persiste y replica sus datos en múltiples servidores. Los detalles de cómo se almacenan, almacenan en caché los datos y se replican son transparentes al servicio de anagrama.
El servidor de anagram se envía con MemoryAdapter ( adapters/MemoryAdapter.js
), que utiliza el mapa de JavaScript para almacenar y consultar datos. Este adaptador tiene una aplicación limitada ya que no proporciona el beneficio de la persistencia en los reinicios del servidor, pero sirve como una buena base para probar y mostrar características del servidor de anagrama.
El proyecto define una interfaz para implementar adaptadores en los adapters/adapter-template.js
. Este archivo se puede usar como Boilerplate para definir nuevos adaptadores.
La interfaz del adaptador está basada en la promesa ya que las API para las tecnologías de almacenamiento tienden a ser asíncronas. Teóricamente, esto agrega tiempo de respuesta ya que las promesas se resuelven a través de la cola de eventos, pero este efecto es insignificante dentro del alcance de una solicitud de red.
Actas
Los métodos add()
y delete()
del adaptador requieren que el almacén subyacente admite transacciones, ya que su lógica implica consultar los datos y luego operar en el almacén en función de los resultados de la consulta.
Resultados de clonación
MECMACEAdapter get()
y each()
métodos devuelve las matrices de valor del mapa directamente al servicio anagramas. Esto requiere diligencia en nombre del código de anagramas para evitar la mutación accidental de los resultados proporcionados por estos métodos.
Clonificar los resultados dentro del MemoryAdapter antes de devolverlos sería un paso sabio para mitigar los errores futuros, garantizar la consistencia de la interfaz y dar menos asombro a los consumidores, pero también implica gastos generales adicionales (aunque probablemente insignificantes).
AnagramService es una clase que proporciona la lógica comercial para el servidor de anagram. Requiere una instancia de un adaptador para pasar a su constructor.
La clase de anagramservice mantiene los métodos de palabras y anagrama e implementa los métodos que admiten directamente la API REST.
Esta clase vive en AnagramService.js
.
server.js
exporta una sola función startServer()
que crea el servidor REST (a través de Restify) e instancia anagramservice.
startServer()
requiere una instancia de adaptador y opcionalmente acepta un número de puerto desde el cual el servicio de servicio y una ruta opcional a un archivo de texto para prepoblar el diccionario desde.
La carne de server.js
es el conjunto de funciones de respuesta del servidor que analizan las solicitudes HTTP individuales, llaman a los métodos de servicio de anagrams relevantes y emiten respuestas con los códigos de respuesta de objetos apropiados y los códigos de respuesta HTTP.
app.js
es el punto de entrada para el servidor Anagram. Es un archivo simple que especifica el adaptador para ejecutar el servicio y una fuente de precarga de datos opcional.
Este es el único archivo que debe cambiar al intercambiar un adaptador por otro.
La versión actual de app.js
ejecuta anagramserver con el dictionary.txt
MemoryAdapter y Preloads.txt al inicio.
A continuación se presentan algunas ideas para desarrollar un servidor de anagrama.
GET /anagrams/:word.json
Devuelve una matriz de palabras que son anagramas de la palabra pasada en la URL.
Si la palabra aprobada en sí no es una palabra conocida (es decir, no en el diccionario), se devuelve una matriz vacía (incluso si se pueden formar anagramas conocidos a partir de la palabra pasada).
Por conveniencia, una palabra pasada como minúscula coincidirá con su forma nominal adecuada.
Ejemplo:
$ 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>
Devuelve una matriz de palabras que son anagramas de la palabra pasada en la URL, pero limitan el número de resultados devueltos .
Ejemplo:
$ 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
Devuelve una matriz de palabras que son anagramas de la palabra pasada en la URL, incluida la palabra de entrada misma .
La palabra de entrada normalmente no se incluye en los resultados de anagrama, ya que una palabra no se considera convencionalmente un anagrama de sí mismo.
$ 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
Devuelve una matriz de palabras que son anagramas de la palabra pasada en la URL, omitiendo los sustantivos propios .
Los sustantivos propios normalmente se incluyen en los resultados de anagrama.
$ 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>
Devuelva todos los conjuntos de anagramas que tienen una cardinalidad mínima y/o máxima (número de anagramas en el conjunto).
Se pueden omitir cardinalitymin o cardinalitymax.
Ejemplos:
$ 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>
Devuelva todos los conjuntos de anagramas que tienen una longitud de palabra mínima y/o máxima.
Se puede omitir la longitud o la longitud.
Ejemplo:
$ 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
Devuelva todos los conjuntos de anagramas con la cardinalidad máxima.
Ejemplo:
$ 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
Devuelva todos los conjuntos de anagramas con la longitud máxima de la palabra.
Ejemplo:
$ 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>
Determine si un conjunto de palabras son anagramas entre sí.
Todas las palabras aprobadas deben ser conocidas (es decir, en el diccionario) para que esto sea cierto.
Ejemplo:
$ 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
El anagrama de regreso solo cuenta. Cada conjunto de anagramas en el diccionario agrega N-1 a este recuento, donde n es el número de anagramas en el conjunto.
Ejemplo:
$ curl -i "http://localhost:3000/anagrams?count=true"
HTTP/1.1 200 OK
Content-Type: application/json
...
{ "counts": { "anagram": 20043 }}
GET /words?count=true
Número de devolución de palabras en el diccionario.
Ejemplo:
$ curl -i "http://localhost:3000/words?count=true"
HTTP/1.1 200 OK
Content-Type: application/json
...
{ "counts": { "word": 235886 }}
GET /words?stats=true
Devuelva algunas estadísticas sobre las palabras en el diccionario.
Ejemplo:
$ 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
Toma una matriz de palabras JSON y las agrega al diccionario.
Ejemplo:
$ 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
Elimine una sola palabra del diccionario.
Si la palabra aprobada en sí no es una palabra conocida (es decir, no en el diccionario), se devuelve un 404
.
Ejemplo:
$ curl -i -X DELETE "http://localhost:3000/words/care.json"
HTTP/1.1 204 No Content
...
DELETE /words/:word.json?includeAnagrams=true
Elimine una sola palabra y todos sus anagramas del diccionario.
Si la palabra aprobada en sí no es una palabra conocida (es decir, no en el diccionario), no se elimina nada y se devuelve un 404
.
Ejemplo:
$ curl -i -X DELETE "http://localhost:3000/words/acre.json?includeAnagrams=true"
HTTP/1.1 204 No Content
...
DELETE /words.json
Borrar todos los contenidos del diccionario.
Ejemplo:
$ curl -i -X DELETE "http://localhost:3000/words.json"
HTTP/1.1 204 No Content
...