2010-09-08 17 views
11

Actualmente estoy en el medio de una aplicación bastante grande basada en preguntas/respuestas (algo así como stackoverflow/answerbag.com) Nosotros está usando SQL (Azure) y nHibernate para acceso a datos y MVC para la aplicación de interfaz de usuario.Dónde/Cómo ajustar Solr en la aplicación MVC de ASP.net (utilizando el patrón nHibernate/Repository)

Hasta ahora, el esquema es más o menos a lo largo de las líneas de la db stackoverflow en el sentido de que tenemos un solo Mensaje mesa (contiene dos preguntas/respuestas)

probablemente va a usar algo en la línea de la interfaz siguiente repositorio:

public interface IPostRepository 
{ 
    void PutPost(Post post); 
    void PutPosts(IEnumerable<Post> posts); 

    void ChangePostStatus(string postID, PostStatus status); 

    void DeleteArtefact(string postId, string artefactKey); 
    void AddArtefact(string postId, string artefactKey); 

    void AddTag(string postId, string tagValue); 
    void RemoveTag(string postId, string tagValue); 

    void MarkPostAsAccepted(string id); 
    void UnmarkPostAsAccepted(string id); 

    IQueryable<Post> FindAll(); 
    IQueryable<Post> FindPostsByStatus(PostStatus postStatus); 
    IQueryable<Post> FindPostsByPostType(PostType postType); 
    IQueryable<Post> FindPostsByStatusAndPostType(PostStatus postStatus, PostType postType); 
    IQueryable<Post> FindPostsByNumberOfReplies(int numberOfReplies); 
    IQueryable<Post> FindPostsByTag(string tag); 
} 

Mi pregunta es: ¿Dónde /¿cómo no encajar Solr en esta opción para facilitar la consulta de estos "mensajes" (voy a estar utilizando solrnet para la comunicación real con Solr)

Idealmente, estaría utilizando DB de SQL como una mera persistente tienda- La mayor parte de las operaciones anteriores IQueryable se movería en una especie de clase SolrFinder (o algo así)

La propiedad Body es el que causa los problemas actualmente: es bastante grande y ralentiza las consultas en sql.

Mi problema principal es, por ejemplo, si alguien "actualiza" una publicación - agrega una nueva etiqueta, por ejemplo, entonces toda la publicación necesitará una nueva indexación. Obviamente, esto requerirá hacer una consulta como esta:

"SELECT * FROM post donde ID = xyz"

Esto, por supuesto, ser muy lento. Solrnet tiene una instalación nHibernate, pero creo que este será el mismo resultado que el anterior?

pensé en una forma de evitar esto, lo que me gustaría su opinión sobre: ​​

  • Adición del ID de una cola (Amazon SQS o algo - me gusta la facilidad de uso con esto)
  • Tener un servicio (o conjunto de servicios) en algún lugar que haga la consulta mencionada, construya el documento y vuelva a agregarlo a solr.

Otro problema que estoy teniendo con mi diseño: Dónde debe el método de "re-indexación" (s) puede llamar desde? El controlador MVC? o debería tener una clase de tipo "PostService", que envuelve la instancia de IPostRepository?

¡Todos los punteros son muy bien recibidos en este caso!

Respuesta

27

En el sitio de comercio electrónico para el que trabajo, utilizamos Solr para proporcionar una rápida facetación y búsqueda en el catálogo de productos. (En términos que no son de Solr geek, esto significa el estilo de enlaces de navegación "ATI Cards (34), NVIDIA (23), Intel (5)" que puede usar para profundizar en catálogos de productos en sitios como Zappos, Amazon, NewEgg y Lowe's)

Esto es porque Solr está diseñado para hacer este tipo de cosas rápido y bien, y tratar de hacer este tipo de cosas de manera eficiente en una base de datos relacional tradicional no va a suceder, a menos que desea comenzar a agregar y eliminar índices sobre la marcha e ir completo EAV, que es sólo tos Magento tos estúpido. Por lo tanto, nuestra base de datos de SQL Server es el almacén de datos "autorizado", y los índices de Solr son "proyecciones" de solo lectura de esos datos.

Estás conmigo hasta ahora porque parece que estás en una situación similar. El siguiente paso es determinar si está bien o no que los datos en el índice de Solr estén un poco obsoletos. Probablemente haya aceptado el hecho de que será un tanto obsoleto, pero las siguientes decisiones son

  • ¿Qué tan añejo está demasiado rancio?
  • ¿Cuándo valoro la velocidad o las características de consulta sobre la estanqueidad?

Por ejemplo, tengo lo que llamo el "trabajador", que es un servicio de Windows que utiliza para ejecutar Quartz.NET C# IJob implementaciones periódicamente. Cada 3 horas, uno de estos trabajos que se ejecuta es el RefreshSolrIndexesJob, y todo lo que hace es enviar un ping a HttpWebRequest a http://solr.example.com/dataimport?command=full-import. Esto se debe a que utilizamos el DataImportHandler incorporado de Solr para realmente absorber los datos de la base de datos SQL; el trabajo solo tiene que "tocar" esa URL periódicamente para que la sincronización funcione. Debido a que DataImportHandler confirma los cambios periódicamente, esto se está ejecutando efectivamente en segundo plano, de forma transparente para los usuarios del sitio web.

Esto significa que la información en el catálogo de productos puede durar hasta 3 horas. Un usuario puede hacer clic en un enlace para "Medio en Stock (3)" en la página del catálogo (ya que este tipo de datos facetados se genera consultando SOLR) pero luego ver en la página de detalles del producto que no hay medios almacenados (ya que página, la información de cantidad es una de las pocas cosas no en caché y consulta directamente en la base de datos). Esto es molesto, pero generalmente raro en nuestro escenario particular (somos un negocio razonablemente pequeño y no que alto tráfico), y se arreglará en 3 horas de todos modos cuando reconstruyamos todo el índice nuevamente desde cero, entonces tenemos aceptado esto como una compensación razonable.

Si puede aceptar este grado de "estancamiento", entonces este proceso de trabajo en segundo plano es una buena forma de hacerlo. Podría tomar el enfoque de "reconstruir todo en pocas horas", o su repositorio podría insertar el ID en una tabla, por ejemplo, dbo.IdentitiesOfStuffThatNeedsUpdatingInSolr, y luego un proceso en segundo plano puede escanear periódicamente esa tabla y actualizar solo esos documentos en Solr si se reconstruye el índice completo desde cero periódicamente no es razonable dado el tamaño o la complejidad de su conjunto de datos.

Un tercer enfoque es tener su repositorio generará un subproceso de fondo que actualiza el índice de Solr en cuanto a que el documento actual, más o menos al mismo tiempo, por lo que los datos sólo está viciado por unos segundos:

class MyRepository 
{ 
    void Save(Post post) 
    { 
     // the following method runs on the current thread 
     SaveThePostInTheSqlDatabaseSynchronously(post); 

     // the following method spawns a new thread, task, 
     // queueuserworkitem, whatevever floats our boat this week, 
     // and so returns immediately 
     UpdateTheDocumentInTheSolrIndexAsynchronously(post); 
    } 
} 

Pero si esto explota por alguna razón, puede perder actualizaciones en Solr, por lo que es una buena idea hacer que Solr haga un "soplarlo todo y actualizarlo" periódicamente, o tener un reaper de fondo. Servicio de tipo trabajador que verifica para datos desactualizados en Solr, todos una vez en una luna azul.

En cuanto a consultar estos datos de Solr, hay algunos enfoques que puede tomar. Una es ocultar el hecho de que Solr existe completamente a través de los métodos del Depósito. Personalmente, no lo recomiendo porque es probable que su esquema de Solr se adapte descaradamente a la interfaz de usuario que accederá a esos datos; Ya tomamos la decisión de usar Solr para proporcionar una fácil facetación, clasificación y visualización rápida de la información, por lo que también podríamos utilizarla en toda su extensión. Esto significa hacerlo explícito en el código cuando queremos acceder a Solr y cuando queremos acceder al objeto de base de datos actualizado, no en caché.

En mi caso, termino utilizando NHibernate para hacer el acceso CRUD (cargando un ItemGroup, continuando con sus reglas de fijación de precios, y luego guardándolo de nuevo), renunciando al patrón de repositorio porque normalmente no veo su valor cuando NHibernate y sus asignaciones ya están abstrayendo la base de datos. (Esta es una elección personal.)

Pero al consultar en los datos, lo sé muy bien si lo estoy usando con fines orientados Catálogo-(me importa velocidad y consultar ) o para la visualización en una tabla en una aplicación administrativa back-end (me importa moneda). Para consultar en el sitio web, tengo una interfaz llamada ICatalogSearchQuery. Tiene un método Search() que acepta un SearchRequest donde defino algunos parámetros - facetas seleccionadas, términos de búsqueda, número de página, número de elementos por página, etc. - y devuelve un SearchResult - facetas restantes, número de resultados, resultados en esta página, etc. Bastante aburrido.

Donde se pone interesante es que la implementación de ese ICatalogSearchQuery está usando una lista de ICatalogSearchStrategy s debajo. La estrategia predeterminada, SolrCatalogSearchStrategy, accede directamente a SOLR a través de un simple HttpWebRequest y analizando el XML en HttpWebResponse (que es mucho más fácil de usar, en mi humilde opinión, que algunas de las bibliotecas de SOLR, aunque pueden haber mejorado desde que La última vez que los miré hace más de un año). Si esa estrategia arroja una excepción o vomita por alguna razón, entonces el DatabaseCatalogSearchStrategy golpea directamente la base de datos SQL, aunque ignora algunos parámetros del SearchRequest, como la creación de facetas o la búsqueda de texto avanzada, ya que es ineficiente hacerlo allí y es la razón principal estamos usando Solr en primer lugar. La idea es que, por lo general, SOLR está respondiendo mis solicitudes de búsqueda rápidamente con todas las funciones, pero si algo explota y SOLR falla, las páginas del catálogo del sitio todavía pueden funcionar en el "modo de funcionalidad reducida" accediendo a la base de datos con un conjunto limitado de características directamente. (Ya que hemos explicitado en el código que se trata de una búsqueda, esta estrategia puede tomar algunas libertades en ignorar algunos de los parámetros de búsqueda sin tener que preocuparse por que afecta a los clientes con demasiada severidad.)

conclusión clave: Lo que es importante es que la decisión de realizar una consulta contra un almacén de datos posiblemente obsoleto versus el almacén de datos autoritativo se ha realizado explicita --si quiero datos rápidos, posiblemente obsoletos con funciones de búsqueda avanzada, uso ICatalogSearchQuery.Si quiero datos lentos y actualizados con la capacidad de insertar/actualizar/eliminar, utilizo las consultas nombradas de NHibernate (o un repositorio en su caso). Y si realizo un cambio en la base de datos de SQL, sé que el servicio de trabajo fuera de proceso actualizará Solr eventualmente, haciendo que las cosas finalmente sean consistentes. (Y si algo era realmente importante, podía transmitir un evento o hacer ping a la tienda SOLR directamente, pidiéndole que lo actualizara, posiblemente en un hilo de fondo si era necesario).

Espero que te dé una idea.

+0

¡excelente respuesta! Utilizo la indexación de Solr de forma ligeramente diferente en el sentido de que la configuración de Solr está configurada para consultar por lotes nuevos registros en un período determinado. De esta forma, no fue necesario escribir ningún código, solo un cambio en la configuración de Solr. Una vez que Solr devuelve coincidencias de búsqueda, actualmente cargo todos los datos para cada partido de NHibernate, aunque pretendo cambiar esto para tener todos los datos de visualización requeridos devueltos por Solr como un punto. Nunca he conseguido que funcione la importación por lotes, pero necesito hacerlo pronto en caso de que el índice corrompa o modifique los campos indexados. – Jordan

2

Si tiene un campo de texto grande (su campo 'cuerpo'), entonces sí, vuelva a indexar en el fondo. Las soluciones que mencionaste (cola o servicio de fondo periódico) servirán.

Los controladores MVC deben ignorar este proceso.

Me di cuenta de que tiene IQueryables en la interfaz del repositorio. SolrNet actualmente no tiene have a LINQ provider. De todos modos, si esas operaciones son todo lo que vas a hacer con Solr (es decirsin facetas), es posible que desee considerar el uso de Lucene.Net en su lugar, que tiene tiene un proveedor de LINQ.

8

Utilizamos solr para consultar una base de datos de productos de gran tamaño. Alrededor de 1 millón de productos y 30 tiendas.

Lo que hicimos fue utilizar activadores en la tabla de productos y en las tablas de valores en nuestro servidor Sql.

Cada vez que se cambia una fila, marca el producto a ser reindexado. Y tenemos un servicio de Windows que toma estos productos y los publica en Solr cada 10 segundos. (Con un límite de 100 productos por lote).

Es información muy eficiente, casi en tiempo real para el stock.

Cuestiones relacionadas