Mongoose how to push an element to a schema of array of arrays - mongodb

I want a schema with an array containing arrays so I have the following schema defined:
runGroupEntries: [
[
{
type: mongoose.Types.ObjectId,
ref: 'User',
require: true
}
]
]
My intention is to have
runGroupEntries[['userId1', 'userId2', 'userId3'], ['userId4', 'userId5', 'userId6], ...]
I initialized the schema using:
for (let i = 0; i < numGroups; ++i) {
event.runGroupEntries.push(undefined);
}
In MongoDB Atlas, it shows:
initialization
It looks fine to me.
The way I insert element is
event.runGroupEntries[runGroup].push(userId);
In this example, runGroup is 0. I was expecting to see
runGroupEntries: [ [ null, "userId" ], [ null ], [ null ], [ null ], [ null ] ]
but the actual result is:
runGroupEntries: [ [ null, [Array] ], [ null ], [ null ], [ null ], [ null ] ],
1st push result
Then I tried to push another userId to event.runGroupEntries[0]. Interestingly, previous array now becomes "userId" but the new element been pushed still shows an array.
runGroupEntries: [
[ null, 5f5c1d95e4f678ce190d5624, [Array] ],
2nd push result
I am really clueless why the pushed element became an array. Any help would be appreciated!

It is indeed a bug in Mongoose. It's fixed now https://github.com/Automattic/mongoose/issues/9429

Related

Group Neighbors by Outgoing Edge Type and Add Self to Result List

I want a result like this:
[
{
"vertex": [ the_vertex_itself ]
},
{
"outgoingEdgeGroup1": [ list_of_outgoing_neighbors_with_edge_type_outgoingEdgeGroup1 ]
},
{
"outgoingEdgeGroup2": [ list_of_outgoing_neighbors_with_edge_type_outgoingEdgeGroup2 ]
}
]
I'm able to get this:
[
{
"outgoingEdgeGroup1": [ list_of_outgoing_neighbors_with_edge_type_outgoingEdgeGroup1 ]
},
{
"outgoingEdgeGroup2": [ list_of_outgoing_neighbors_with_edge_type_outgoingEdgeGroup2 ]
}
]
With the following query:
g.V('{unitId}').outE().group().by(label()).by(inV().fold())
But how would I append on the target vertex itself?
One way is just to use a union. If you need a more complete key/value type of structure that can be created also by adding project steps or nested group steps.
Using the air-routes data set:
gremlin> g.V(44).union(identity().values('city'),inE().group().by(label()).by(outV().fold())).fold()
==>[Santa Fe,[contains:[v[3742],v[3728]],route:[v[13],v[31],v[20],v[8]]]]

Postgresql get keys from array of objects in JSONB field

Here' a dummy data for the jsonb column
[ { "name": [ "sun11", "sun12" ], "alignment": "center", "more": "fields" }, { "name": [ "sun12", "sun13" ], "alignment": "center" }, { "name": [ "sun14", "sun15" ] }]
I want to fetch all the name keys value from jsonb array of objects...expecting output -
[ [ "sun11", "sun12" ], [ "sun12", "sun13" ], [ "sun14", "sun15" ] ]
The problem is that I'm able to fetch the name key value by giving the index like 0, 1, etc
SELECT data->0->'name' FROM public."user";
[ "sun11", "sun12" ]
But I'm not able to get all the name keys values from same array of object.I Just want to get all the keys values from the array of json object. Any help will be helpful. Thanks
demo:db<>fiddle (Final query first, intermediate steps below)
WITH data AS (
SELECT '[ { "name": [ "sun11", "sun12" ], "alignment": "center", "more": "fields" }, { "name": [ "sun12", "sun13" ], "alignment": "center" }, { "name": [ "sun14", "sun15" ] }]'::jsonb AS jsondata
)
SELECT
jsonb_agg(elems.value -> 'name') -- 2
FROM
data,
jsonb_array_elements(jsondata) AS elems -- 1
jsonb_array_elements() expands every array element into one row
-> operator gives the array for attribute name; after that jsonb_agg() puts all extracted arrays into one again.
my example
SELECT DISTINCT sub.name FROM (
SELECT
jsonb_build_object('name', p.data->'name') AS name
FROM user AS u
WHERE u.data IS NOT NULL
) sub
WHERE sub.name != '{"name": null}';

How to fix geojson to satisfy the needs of a mongodb 2dsphere index

I have ~400K documents in a mongo collection, all with geometry of type:Polygon. It is not possible to add a 2dsphere index to the data as it currently stands because the geometry apparently has self-intersections.
In the past we had a hacky workaround which was to compute the bounding box of the geometry on a mongoose save hook and then index that rather than the geometry itself, but we would like to simplify things and just use the actual geometry.
So far I have tried using turf as follows (this is the body of a function called fix):
let geom = turf.polygon(geometry.coordinates);
geom = turf.simplify(geom, { tolerance: 1e-7 });
geom = turf.cleanCoords(geom);
geom = turf.unkinkPolygon(geom);
geom = turf.combine(geom);
return geom.features[0].geometry;
The most important function there is the unkinkPolygons which I hoped would do exactly what I wanted, i.e. make the geometry nice enough to be indexed. The simplify is possibly not helpful but I added it in for good measure. The clean is there because unkink complained about its input, and the combine is there to turn an array of Polygons into a single MultiPolygon. Actually, unkink still wasn't happy with it's inputs, so I had to write a hacky function as follows that jitters duplicated vertices, this modifies the geom before passing to unkink:
function jitterDups(geom) {
let coords = geom.geometry.coordinates;
let points = new Set();
for (let ii = 0; ii < coords.length; ii++) {
// last coords is allowed to match first, not sure if it must match.
let endsMatch = coords[ii][0].join(",") === coords[ii][coords[ii].length - 1].join(",");
for (let jj = 0; jj < coords[ii].length - (endsMatch ? 1 : 0); jj++) {
let str = coords[ii][jj].join(",");
while (points.has(str)) {
coords[ii][jj][0] += 1e-8; // if you make this too small it doesn't do the job
if (jj === 0 && endsMatch) {
coords[ii][coords[ii].length - 1][0] = coords[ii][jj][0];
}
str = coords[ii][jj].join(",");
}
points.add(str);
}
}
}
However, even after all of that mongo still complains.
Here is some sample raw Polygon input:
{ type: "Polygon", coordinates: [ [ [ -0.027542009179339, 51.5122867222457 ], [ -0.027535822940572, 51.512281465421 ], [ -0.027535925691804, 51.5122814221859 ], [ -0.027589474043984, 51.5122605515771 ], [ -0.027638484531731, 51.5122996934574 ], [ -0.027682911101528, 51.5123351881505 ], [ -0.027689915350493, 51.5123872384419 ], [ -0.027672409315982, 51.5123868001613 ], [ -0.027667905522642, 51.5123866344944 ], [ -0.027663068941865, 51.5123864992013 ], [ -0.02764931654289, 51.512375566682 ], [ -0.027552504539425, 51.5122983194123 ], [ -0.027542009179339, 51.5122867222457 ] ], [ [ -0.027542009179339, 51.5122867222457 ], [ -0.027557948301911, 51.5122984109658 ], [ -0.027560309178214, 51.5123001412876 ], [ -0.027542009179339, 51.5122867222457 ] ] ] }
And that same data after it has passed through the above fixing pipeline:
{ type: "MultiPolygon", coordinates: [ [ [ [ -0.027560309178214, 51.5123001412876 ], [ -0.02754202882236209, 51.51228674396312 ], [ -0.027542009179339, 51.5122867222457 ], [ -0.027535822940572, 51.512281465421 ], [ -0.027589474043984, 51.5122605515771 ], [ -0.027682911101528, 51.5123351881505 ], [ -0.027689915350493, 51.5123872384419 ], [ -0.027663068941865, 51.5123864992013 ], [ -0.027552504539425, 51.5122983194123 ], [ -0.02754202884162257, 51.51228674398443 ], [ -0.027557948301911, 51.5122984109658 ], [ -0.027560309178214, 51.5123001412876 ] ] ], [ [ [ -0.02754202884162257, 51.51228674398443 ], [ -0.02754202882236209, 51.51228674396312 ], [ -0.027541999179339, 51.5122867222457 ], [ -0.02754202884162257, 51.51228674398443 ] ] ] ] }
And here is the relevant bit of the error that is spat out by the index creation:
Edges 0 and 9 cross.
Edge locations in degrees: [-0.0275603, 51.5123001]-[-0.0275420, 51.5122867] and [-0.0275420, 51.5122867]-[-0.0275579, 51.5122984]
"code" : 16755,
"codeName" : "Location16755"
My question is: is there a bug in turf, or is it not doing what I need here in terms of keeping mongo happy? Also is there any documentation on exactly what the 2dshpere index needs in terms of "fixing"? Also, does anyone have suggestions as to what other tools I might use to fix the data, e.g. mapshaper or PostGIS's ST_MakeValid.
Note that once the existing data is fixed I also need a solution for fixing new data on the fly (ideally something that works nice with node).
Mongo Version: 3.4.14 (or any later 3.x)
The problem here is not that the polygon is intersecting itself, but rather that you have a (tiny) hole in the polygon, composed of 4 points, which shares a point with the exterior. So the hole "touches" the exterior, not intersects with it, but this is not allowed.
You can fix such cases using Shapely buffer with a tiny value, e.g.:
shp = shapely.geometry.shape({ "type": "Polygon", "coordinates": [ [ [ -0.027542009179339, 51.5122867222457 ], [ -0.027535822940572, 51.512281465421 ], [ -0.027535925691804, 51.5122814221859 ], [ -0.027589474043984, 51.5122605515771 ], [ -0.027638484531731, 51.5122996934574 ], [ -0.027682911101528, 51.5123351881505 ], [ -0.027689915350493, 51.5123872384419 ], [ -0.027672409315982, 51.5123868001613 ], [ -0.027667905522642, 51.5123866344944 ], [ -0.027663068941865, 51.5123864992013 ], [ -0.02764931654289, 51.512375566682 ], [ -0.027552504539425, 51.5122983194123 ], [ -0.027542009179339, 51.5122867222457 ] ], [ [ -0.027542009179339, 51.5122867222457 ], [ -0.027557948301911, 51.5122984109658 ], [ -0.027560309178214, 51.5123001412876 ], [ -0.027542009179339, 51.5122867222457 ] ] ] })
shp = shp.buffer(1e-12, resolution=0)
geojson = shapely.geometry.mapping(shp)

Limitations on mongodb Polygon

I'm trying to insert a Polygon on a mongodb document
polygons: {
type: "Polygon",
coordinates: [
[
[ -104.6093679, 50.449794 ],
[ -104.6093679, 50.449794 ],
[ -104.6093863, 50.449794 ],
[ -104.6093863, 50.449794 ],
[ -104.6093679, 50.449794 ]
]
]
}
But mongodb throws this exception: "Can't extract geo keys from object, malformed geometry?" I also checked the correctness of index and verified that with the same structure but different data, insert works well. My question is: are there some limitations on Polygon area that mongodb can manage?

syntax issue ? passing objects as parameters in neo4j cypher statement

I am running a query like this using the REST API to the transaction endpoint:
{
"statements" : [{"statement":"MATCH (n)-[r]-(m) WHERE id(n) IN {diagramnodes} return [type(r),labels(m)] ",
"parameters" :{
"diagramnodes" : [28]
}}]
}
which returns the expected result:
{
"commit": "http://myserver:7474/db/data/transaction/542/commit",
"results": [
{
"columns": [
"[type(r),labels(m)]"
],
"data": [
{
"row": [
[
"CONTAINS",
[
"Sentence"
]
]
]
},
{
"row": [
[
"CONTAINS",
[
"Prologram",
"Diagram"
]
]
]
},
.......
]
}
],
"transaction": {
"expires": "Sun, 07 Sep 2014 17:50:11 +0000"
},
"errors": []
}
When adding another parameter and a filter to limit the types of rels that are returned:
{"statements": [{
"statement": "MATCH (n)-[r]-(m) WHERE id(n) IN {diagramnodes} AND [type(r),labels(m)] IN {includerels} return r ",
"parameters": {
"diagramnodes": [28],
"includerels": [
[
"CONTAINS",
[
"Prologram",
"Diagram"
]
],
[
"HAS_TARGET",
["Term"]
]
]
}
}]}
it does not return any results. Why?
I found a workaround, by concatenating the reltype and labels, and comparing it to a collection of primitive types. This is the cypher (added some CRLF to make it easier to read)
{
"statements" : [{"statement":"
MATCH (n)-[r]-(m)
WHERE id(n) IN {diagramnodes}
WITH type(r) as rtype, REDUCE(acc = '', p IN labels(m)| acc + ' '+ p) AS mlabels,m
WITH rtype+mlabels As rtypemlabels,m
WHERE rtypemlabels IN {includerels}
RETURN rtypemlabels,id(m) ",
"parameters" :{
"diagramnodes" : [28],
"includerels": ["HAS_TARGET Term","CONTAINS Sentence","CONTAINS Prologram Diagram"]
}}]
}
Note 1 : type(r)+REDUCE(acc = '', p IN labels(m)| acc + ' '+ p) does not work, you have to insert an additional WITH
Note 2 : comparing a collection of nested objects with an IN clause should be possible and remains on my wish list. ;)
IN operations very probably only work for collections of primitive values.
what you can try to do is to rewrite it into ALL(x in coll WHERE expr(x)) predicate.
for an input like:
[["CONTAINS",["Prologram","Diagram"]],
["HAS_TARGET",["Term"]]]
you can try:
ALL(entry in {includerels} WHERE type(r) = entry[0] AND ALL(l in labels(n) WHERE l in entry[1]))
You can use UNWIND on your parameters array instead of using IN. Depending on your data, you might have to use DISTINCT as well. But UNWIND works well for me.