2012-04-05 13 views
5

Tengo un proyecto GAE escrito en Java y tengo algunas ideas sobre el HRD y un problema que no estoy seguro de cómo resolverlo.Google App Engine consulta HRD sin antecesor

Básicamente tengo usuarios en mi sistema. Un usuario consiste en un ID de usuario, un nombre de usuario, un correo electrónico y una contraseña. Cada vez que creo un nuevo usuario, quiero verificar que no haya un usuario con el mismo ID de usuario (nunca debería haberlo), nombre de usuario o correo electrónico.

El ID de usuario es la clave, por lo que creo que hacer una con esto será consistente. Sin embargo, cuando realizo una consulta (y utilizo un filtro) para encontrar posibles usuarios con el mismo nombre de usuario o correo electrónico, no puedo estar seguro de que los resultados sean consistentes. Entonces, si alguien creó un usuario con el mismo nombre de usuario o correo electrónico hace un par de segundos, es posible que no lo encuentre con mi consulta. Entiendo que los antepasados ​​se utilizan para solucionar este problema, pero ¿qué sucede si no tengo un antepasado para utilizar para la consulta? El usuario no tiene un padre.

Estaría encantado de escuchar sus pensamientos sobre esto, y lo que se considera la mejor práctica en situaciones como estas. Estoy usando Objectify para GAE si eso cambia algo.

+1

me hizo una pregunta que puede resultar muy útil. (http://stackoverflow.com/questions/6584435/how-can-i-create-two-unique-queria-fields-for-a-gae-datastore-data-model) Yo también necesitaba almacenar información única para mis usuarios. En mi caso, necesitaba almacenar tanto un correo electrónico único como una identificación de usuario única por usuario. Esto es un poco difícil con el HRD, pero llegué a una solución confiable. ... cont ... – RLH

+0

El único problema con mi situación es que la implementación de mi creación de cuenta (ver mi respuesta) no se escalará bien. Eso estuvo bien en mi circunstancia porque mi aplicación GAE es muy pequeña y tiene un flujo lento de nuevos usuarios (1 o 2 por mes). Además, esta información está en Python, pero el código es simple-- usted debería ser capaz de re -traducirlo a Java con relativa facilidad. – RLH

+0

@RLH Gracias por su aporte, siempre es interesante ver diferentes soluciones, pero no creo que su solución funcione bien en mi caso. – Joel

Respuesta

7

No recomendaría el uso del correo electrónico ni de ninguna otra clave natural para su entidad de usuario. Los usuarios cambian sus direcciones de correo electrónico y no desea terminar reescribiendo todas las referencias de claves externas en su base de datos cada vez que alguien cambia su correo electrónico.

He aquí una breve reseña de cómo puedo solucionar este problema:

https://groups.google.com/d/msg/google-appengine/NdUAY0crVjg/3fJX3Gn3cOYJ

crear una entidad EmailLookup separada cuya @Id es la forma normalizada de una dirección de correo electrónico (acabo en minúsculas todo - técnicamente incorrecto, pero ahorra mucho dolor cuando los usuarios capitalizan accidentalmente [email protected]). Mi EmailLookup se ve así:

@Entity(name="Email") 
public class EmailLookup { 
    /** Use this method to normalize email addresses for lookup */ 
    public static String normalize(String email) { 
     return email.toLowerCase(); 
    } 

    @Id String email; 
    @Index long personId; 

    public EmailLookup(String email, long personId) { 
     this.email = normalize(email); 
     this.personId = personId; 
    } 
} 

También hay un campo de correo electrónico (no normalizada) en mi entidad Usuario, el cual utilizo para enviar correos electrónicos salientes (preservar caso por si acaso es importante para alguien). Cuando alguien crea una cuenta con un correo electrónico en particular, cargo/creo el EmailLookup y las entidades de usuario con la clave en una transacción XG. Esto garantiza que cualquier dirección de correo electrónico individual será única.

La misma estrategia se aplica a cualquier otro tipo de valor único; Identificación de Facebook, nombre de usuario, etc.

+1

Puedo responder por este método. Recomiendo 100% 'long' ids generados automáticamente para todas las entidades" actuales ", y IDs de cadena para mejoradores de coherencia/consulta como este –

+0

Existen claves naturales perfectamente legítimas para usar en muchas aplicaciones, y su uso a menudo puede salvar la necesidad hacer consultas, a favor de operaciones de obtención de datos mucho más eficientes. Por ejemplo, la clave natural de una entidad de "página" es su URI. –

+1

Claro, hay muchas entidades para las cuales una clave natural tiene sentido. Solo afirmo que la entidad de usuario del OP no es una de ellas :-) – stickfigure

3

Una forma de evitar el HRD eventual consistency, es usar get en lugar de query. Para poder hacer esto es necesario generar ID naturales, p. genere ID que consiste en datos que recibe a petición: correo electrónico y nombre de usuario.

Dado que get en HRD tiene una gran consistencia, usted podrá verificar de manera confiable si el usuario ya existe.

Por ejemplo una identificación legible naturales sería:

String naturalUserId = userEmail + "-" + userName; 

Nota: en la práctica, mensajes de correo electrónico son únicos. Entonces esta es una buena identificación natural por sí misma. No es necesario agregarle un nombre de usuario inventado.

+1

Me gusta la simplicidad de esta solución, pero no me gusta la idea de tener que cambiar la clave si el correo electrónico o el nombre de usuario cambian. – Joel

-1

También puede habilitar transacciones entre grupos (consulte https://developers.google.com/appengine/docs/java/datastore/overview#Cross_Group_Transactions) y luego, en una transacción, busque al usuario y cree uno nuevo, si eso le sirve.

+1

De ese documento: "De forma similar a las transacciones de un solo grupo, no puede realizar una consulta que no sea ancestro en una transacción XG". –

+0

Bueno, entonces hay un problema. Tal vez intente la consulta antes de la transacción, procese la operación de guardar y, luego, realice una consulta externa para verificar si solo hay un resultado del tipo deseado. Sin embargo, debe costar mucho más tiempo de CPU, este tipo de verificación doble, cuando no es muy probable que las personas completen la misma dirección de correo electrónico. Además, esta solución se ve bastante bien: http://stackoverflow.com/a/10032511/473180 – themarketka

+0

Además, tampoco se garantiza que la verificación doble sea consistente, por lo que aún podría tener duplicados sin saberlo. Básicamente, las consultas que no sean ancestros nunca se pueden usar para verificar la coherencia con el HRD. –

-1

Recomienda evitar un campo indexado y consultar a menos que tenga otros usos para él. Esto es lo que he hecho antes (Python) usando key_name (ya que los ID de la entidad deben ser enteros). Fácil de utilizar tanto el nombre_tecla o la identificación de otras entidades que tienen que enlaza con Usuario:

username = self.request.get('username') 
usernameLower = username.lower() 
rec = user.get_by_key_name(usernameLower) 
if rec is None: 
    U = user(
     key_name = usernameLower, 
     username = username, 
     etc...) 
    U.put() 
else: 
    self.response.out.write(yourMessageHere) 
+0

Esto no funciona, ya que otro usuario podría insertar un nuevo nombre de usuario de registro == username username antes de la puesta. – supercobra