programacion - manual de r estadistica en español
Uso múltiple del operador posicional `$` para actualizar matrices anidadas (2)
Esta pregunta está estrechamente relacionada con esta y consideraré los consejos dados con respecto al diseño del esquema en un contexto NoSQL, pero tengo curiosidad por comprender esto:
Preguntas reales
Supongamos que tiene el siguiente documento:
_id : 2 abcd
name : 2 unittest.com
paths : 4
0 : 3
path : 2 home
queries : 4
0 : 3
name : 2 query1
url : 2 www.unittest.com/home?query1
requests: 4
1 : 3
name : 2 query2
url : 2 www.unittest.com/home?query2
requests: 4
Básicamente, me gustaría saber
si es posible usar el operador
$
posicional de MongoDB ( detalles ) varias veces, o ponerlo de otra manera, en escenarios de actualización que involucran estructuras de matriz / documento con un "grado de anidamiento" mayor que 1:{ <update operator>: { "paths.$.queries.$.requests" : value } }
( no funciona )en lugar de "solo", puede usar
$
una vez para una matriz de nivel superior y estar obligado a usar índices explícitos para matrices en "niveles superiores":{ <update operator>: { "paths.$.queries.0.requests" : value } }
) ( funciona )si es posible, cómo sería la sintaxis R correspondiente.
A continuación encontrará un ejemplo reproducible. Traté de ser lo más conciso posible.
Ejemplo de código
Conexión a la base
require("rmongodb")
db <- "__unittest"
ns <- paste(db, "hosts", sep=".")
# CONNCETION OBJECT
con <- mongo.create(db=db)
# ENSURE EMPTY DB
mongo.remove(mongo=con, ns=ns)
Documento de ejemplo
q <- list("_id"="abcd")
b <- list("_id"="abcd", name="unittest.com")
mongo.insert(mongo=con, ns=ns, b=b)
q <- list("_id"="abcd")
b <- list("$push"=list(paths=list(path="home")))
mongo.update(mongo=con, ns, criteria=q, objNew=b)
q <- list("_id"="abcd", paths.path="home")
b <- list("$push"=list("paths.$.queries"=list(
name="query1", url="www.unittest.com/home?query1")))
mongo.update(mongo=con, ns, criteria=q, objNew=b)
b <- list("$push"=list("paths.$.queries"=list(
name="query2", url="www.unittest.com/home?query2")))
mongo.update(mongo=con, ns, criteria=q, objNew=b)
Actualización de matrices anidadas con índice de posición explícito (trabajos)
Esto funciona, pero implica un índice explícito para las queries
matriz de segundo nivel (anidadas en un elemento subdoc de paths
de matriz):
q <- list("_id"="abcd", paths.path="home", paths.queries.name="query1")
b <- list("$push"=list("paths.$.queries.0.requests"=list(time="2013-02-13")))
> mongo.bson.from.list(b)
$push : 3
paths.$.queries.0.requests : 3
time : 2 2013-02-13
mongo.update(mongo=con, ns, criteria=q, objNew=b)
res <- mongo.find.one(mongo=con, ns=ns, query=q)
> res
_id : 2 abcd
name : 2 unittest.com
paths : 4
0 : 3
path : 2 home
queries : 4
0 : 3
name : 2 query1
requests : 4
0 : 3
time : 2 2013-02-13
url : 2 www.unittest.com/home?query1
1 : 3
name : 2 query2
url : 2 www.unittest.com/home?query2
Actualización de matrices anidadas con índices $
posicionales (no funciona)
Ahora, me gustaría sustituir el 0
explícito con el operador posicional $
tal como lo hice para que el servidor encuentre el elemento subdoc deseado de las rutas de los vectores ( paths.$.queries
).
AFAIU la documentación , esto debería funcionar ya que lo crucial es especificar un selector de consultas "correcto":
El operador $ posicional, cuando se usa con el método update () y actúa como un marcador de posición para la primera coincidencia del selector de consulta de actualización:
Creo que especifiqué un selector de consultas que encuentra el elemento anidado correcto (debido a la parte paths.queries.name="query1"
):
q <- list("_id"="abcd", paths.path="home", paths.queries.name="query1")
Supongo que traducido a la sintaxis "MongoDB simple", el selector de consultas se parece algo a esto
{ _id: abcd, paths.path: home, paths.queries.name: query1 }
que me parece un selector de consulta válido. De hecho, coincide con el elemento / documento deseado:
> !is.null(mongo.find.one(mongo=con, ns=ns, query=q))
[1] TRUE
Mi idea era que si funciona en el nivel superior, ¿por qué no debería funcionar también para niveles más altos (siempre que el selector de consultas apunte a los componentes anidados a la derecha)?
Sin embargo, al servidor no parece gustarle un uso anidado o múltiple de $
:
b <- list("$push"=list("paths.$.queries.$.requests"=list(time="2013-02-14")))
> mongo.bson.from.list(b)
$push : 3
paths.$.queries.$.requests : 3
time : 2 2013-02-14
> mongo.update(mongo=con, ns, criteria=q, objNew=b)
[1] FALSE
No estoy seguro de si no funciona porque MongoDB no es compatible con esto o si no obtuve la sintaxis R correcta.
El operador posicional solo admite un nivel de profundidad y solo el primer elemento coincidente.
Existe un JIRA rastreable para el tipo de comportamiento que desea aquí: https://jira.mongodb.org/browse/SERVER-831
No estoy seguro de si permitirá más de un partido, pero creo que lo hará debido a la dinámica de cómo tendrá que funcionar.
En caso de que pueda ejecutar su consulta desde el shell de MongoDB, puede eludir esta limitación aprovechando el cursor de MongoDB para cada función ( http://docs.mongodb.org/manual/reference/method/cursor.forEach/ )
Aquí hay un ejemplo con 3 matrices anidadas:
var collectionNameCursor = db.collection_name.find({...});
collectionNameCursor.forEach(function(collectionDocument) {
var firstArray = collectionDocument.firstArray;
for(var i = 0; i < firstArray.length; i++) {
var secondArray = firstArray[i].secondArray;
for(var j = 0; j < secondArray.length; j++) {
var thirdArray = secondArray[j].thirdArray;
for(var k = 0; k < thirdArray.length; k++) {
//... do some logic here with thirdArray''s elements
db.collection_name.save(collectionDocument);
}
}
}
});
Tenga en cuenta que esto es más una solución de una sola vez que un código de producción, pero hará el trabajo si tiene que escribir un script de reparación.