Meteor query based on the value of elements in an array inside an object - meteor

I am new to meteor and mongoDB and have been searching for an answer to this question for some time without any luck.
I have multiple documents in MongoDB similar to the one below:
{
"_id" : ObjectId("5abac4ea0c31d26804421371"),
"Points" : [
{
"Value" : 6.869752766626993,
"Time" : 1522284528946
},
{
"Value" : 3.9014587731230477,
"Time" : 1522284543946
},
{
"Value" : 1.2336926618519772,
"Time" : 1522284558946
},
{
"Value" : 6.504837583667155,
"Time" : 1522284573946
},
{
"Value" : 9.824138227740864,
"Time" : 1522284588946
},
{
"Value" : 9.707480757899235,
"Time" : 1522284603946
},
{
"Value" : 4.6122167850338105,
"Time" : 1522284618946
}
]
}
How can I implement a query in meteor that returns an array containing all the Points from all documents with 'Time' field greater than certain value?

As Jankapunkt has pointed out in his comment, it might be a lot easier and better if you created a new collection Points where each document includes only Value and Time attributes. The given example would then become seven separate documents rather than a single array.
It does nevertheless happen, that we want to query documents according to some inner values, e.g. attributes in objects in arrays.
Taken from the mongodb documentation on querying embedded documents, we can just use dot notation for this.
If you do not know the index position of the document nested in the array, concatenate the name of the array field, with a dot (.) and the name of the field in the nested document.
Such as for your question (assuming Points to be the name of your collection):
db.points.find( { 'Points.Time': { $gte: 123412341234 } } )
Which looks almost identical in Meteor:
Points.find({ 'Points.Time': { $gte: 123412341234 } })

Related

Extract values from web service JSON response with JSONPath

I have a JSON response from web service that looks something like this :
[
{
"id":4,
"sourceID":null,
"subject":"SomeSubjectOne",
"category":"SomeCategoryTwo",
"impact":null,
"status":"completed"
},
{
"id":12,
"sourceID":null,
"subject":"SomeSubjectTwo",
"category":"SomeCategoryTwo",
"impact":null,
"status":"assigned"
}
]
What I need to do is extract the subjects from all of the entities by using JSONPATH query.
How can I get these results :
Subject from the first item - SomeSubjectOne
Filter on specific subject value from all entities (SomeSubjectTwo for example)
Get Subjects from all entities
Goessner's orinial JSONPath article is a good reference point and all implementations more or less stick to the suggested query syntax. However, implementations like Jayway JsonPath/Java, JSONPath-Plus/JavaScript, flow-jsonpath/PHP may behave a little differently in some areas. That's why it can be important to know what implementation you are actually using.
Subject from the first item
Just use an index to select the desired array element.
$.[0].subject
Returns:
SomeSubjectOne
Specific subject value
First, go for any elements .., check those with a subject [?(#.subject] and use == '..' for comparison.
$..[?(#.subject == 'SomeSubjectTwo')]
Returns
[ {
"id" : 12,
"sourceID" : null,
"subject" : "SomeSubjectTwo",
"category" : "SomeCategoryTwo",
"impact" : null,
"status" : "assigned" } ]*
Get all subjects
$.[*].subject
or simply
$..subject
Returns
[ "SomeSubjectOne", "SomeSubjectTwo" ]

Does Firebase "GeoFire" Enforce Identical Keys Between Nodes with Geo and non-Geo Data?

The database structure for a Firebase "GeoFire" node must look like this (source)
"items" : {
<itemId> : {
"someData" : "someData",
...
}
},
"items_location" : {
<itemId> : {
<geofireData> ...
}
}
But one limitation of Geofire is that only single points can be stored and queried, and no objects like polygons. That's easy to work around - my code can query nearby points, and then reassemble the simple rectangles based on having the same key.
But in splitting my rectangles into individual points, I've created a GeoFire key with the following format
ABCD_0
Where ABCD is the original Primary Key of the rectangle, and _0 indicates which corner, so as to have each point with a unique key. One rectangle is represented in GeoFire as
"items" : {
<ABCD_0> : {<objectData>},
<ABCD_1> : {<objectData>},
<ABCD_2> : {<objectData>},
<ABCD_3> : {<objectData>}
},
"items_location" : {
<ABCD_0> : {<geofireData 0>},
<ABCD_1> : {<geofireData 1>},
<ABCD_2> : {<geofireData 2>},
<ABCD_3> : {<geofireData 3>}
}
But then to force identical keys in items and items_location, <objectData> is 4x redundant.
In order to decrease data volume, I'd like to use the original Primary Key in the items node, and then replicate the key with the _X structure for the items_location node. The App would then query GeoFire, get a list of (4) nearby keys, and then string-parse ABCD_X into ABCD, which it would use for the subsequent query.
"items" : {
<ABCD> : {<objectData>},
},
"items_location" : {
<ABCD_0> : {<geofireData 0>},
<ABCD_1> : {<geofireData 1>},
<ABCD_2> : {<geofireData 2>},
<ABCD_3> : {<geofireData 3>}
}
Will this break how GeoFire stores, indexes, retrieves and offlines data?
I'm especially concerned about how small sets of data is synchronized offline for individual apps. The entire geo-dataset is too large for a single app to store in its entirety offline.

Meteor aggregate group by the month on Date.now() property value

A Meteor server code tries to group documents by the month. So that the count of all the documents in each month is given.
The documents have createdAt = Date.now(); property and meteorhacks:aggregate
is installed. Any Suggestions? thx
What I considered:
1) Transform the collection adding new property `MMYYYY: 6-digit-number' and group by that.
Your consideration is a good one if you plan to aggregate often. It would certainly be faster if you wrote a year and month string in the doc when you create it. Keep in mind that you will have to remember to keep those strings up to date if you modify the corresponding date field.
If you won't be aggregating very often, then it's probably not worth the effort. Just convert your epoch time values to new Date objects and leverage the Date aggregation operators in your aggregation pipeline. Here is an example.
var Metrics = new Mongo.Collection('metrics');
Metrics.aggregate([
{$project: {
createdAtDate: { $add: [new Date(0), "$createdAt"] }
}},
{$project : {
year : {$year : "$createdAtDate"},
month : {$month : "$createdAtDate"}
}},
{$group : {
_id : {year : "$year", month : "$month"},
count : {$sum : 1}
}}
]);
If you go with a date string (where the document field called "createdAtString" is MMYYYY) then your pipeline would look like this instead.
var Metrics = new Mongo.Collection('metrics');
Metrics.aggregate([
{$group : {
_id : "$createdAtString",
count : {$sum : 1}
}}
]);

difference between the value returned by count and status api by elasticsearch

Currently I'm counting the number of documents in an index in elasticsearch.
When I used count api, it returned:
{
"count" : 86873223,
"_shards" : {
"total" : 29,
"successful" : 29,
"failed" : 0
}
}
However when I used status api, it returned:
{
...,
"docs" : {
"num_docs" : 333638298,
"max_doc" : 429446807,
"deleted_docs" : 95808509
},
...,
}
I'm wondering why there is a huge difference between the two.
Any hint?
Btw, I'm using the nested objects heavily. Could this be the reason?
Thanks!
Each nested document is a internal Lucene document by itself.
Hence you will see this number added to the entire document set.

No Idea how to create a specific MapReduce in CouchDB

I've got 3 types of documents in my db:
{
param: "a",
timestamp: "t"
} (Type 1)
{
param: "b",
partof: "a"
} (Type 2)
{
param: "b",
timestamp: "x"
} (Type 3)
(I can't alter the layout...;-( )
Type 1 defines a start timestamp, it's like the start event. A Type 1 is connected to several Type 3 docs by Type 2 documents.
I want to get the latest Type 3 (highest timestamp) and the corresponding type 1 document.
How may I organize my Map/Reduce?
Easy. For highly relational data, use a relational database.
As user jhs stated before me, your data is relational, and if you can't change it, then you might want to reconsider using CouchDB.
By relational we mean that each "type 1" or "type 3" document in your data "knows" only about itself, and "type 2" documents hold the knowledge about the relation between documents of the other types. With CouchDB, you can only index by fields in the documents themselves, and going one level deeper when querying using includedocs=true. Thus, what you asked for cannot be achieved with a single CouchDB query, because some of the desired data is two levels away from the requested document.
Here is a two-query solution:
{
"views": {
"param-by-timestamp": {
"map": "function(doc) { if (doc.timestamp) emit(doc.timestamp, [doc.timestamp, doc.param]); }",
"reduce": "function(keys, values) { return values.reduce(function(p, c) { return c[0] > p[0] ? c : p }) }"
},
"partof-by-param": {
"map": "function(doc) { if (doc.partof) emit(doc.param, doc.partof); }"
}
}
}
You query it first with param-by-timestamp?reduce=true to get the latest timestamp in value[0] and its corresponding param in value[1], and then query again with partof-by-param?key="<what you got in previous query>". If you need to fetch the full documents together with the timestamp and param, then you will have to play with includedocs=true and provide with the correct _doc values.

Resources