Publish Embedded Document Array to Meteor - meteor

I have meteor application made up of "notepads", each containing an array of "notes" which can be inserted into at any position, deleted from or have rows edited. This array is contained within an object with a variety of other information (ex. name, users, etc). Each object in my primary document will contain one of these arrays. For example:
{
"_id": "1234",
"name": "NotePad123",
"notes": [ {note: "this is my first test note"},
{note: "this is my second test note"},
{note: "this is my third test note"} ]
},{
"_id": "4321",
"name": "NotePad321",
"notes": [ {note: "noteA"},
{note: "noteB"},
{note: "noteC"} ]
}
Is there any way I can pass the "notes" as its own collection to my client so that the client can directly edit it as if it were not embedded? I am worried about a performance hit if I need to be passing the full notes array to the server every time I want to update it as there may be many updates it could become quite large.
I realize that I could create a new document and reference it, as described here, but this could become quite hectic with many "notepads" as ordering is important and I will have many rows associated with each of my primary objects.

You can make a client-side collection that you put the notes in. Then, call a method to make the changes once you want to save.
Here's how you make a client-side collection:
var notes = new Meteor.Collection(null)

Related

Drupal 8 jsonapi: How to change the structure of returned json relationships included array?

I am sending a request to the jsonapi node endpoint, and using the following parameter to include the "relationship" objects of those nodes for user (post author data) and user picture (avatar):
include=uid,uid.user_picture
This returns to me the json data of the nodes as well as all of the relationship objects lumped together (regardless of type) into the "included" array. The included array ends up looking like this by default:
"included": [
{
"type": "user--user",
"id": "4c717273-f903-4ffa-abe2-b5e5709ad727",
"attributes": {
"display_name": "cherylm1234",
...etc...
}
},
{
"type": "file--file",
"id": "4c717273-f903-4ffa-abe2-b5e5709ad727",
"attributes": {
"filename": "Cheryl-Fun.jpg",
...etc...
}
}
]
Context: We are trying to integrate jsonapi with an iOS app as the client/consumer.
My Problem: It's difficult to write up data models in the iOS client for these relationships (without looping and sorting every response) since they are all lumped into the same level of the "included" array. Also a problem is that each "type" will have its own set of attributes/fields so data models in iOS need to be built based on "type".
Question: Is it possible to change this structure so that all included objects are sorted by type?
I would like for the "included" array to be indexed by "type" where all objects of that type would be inside that index. Maybe something like:
included['user--user'] = [
{user-object},
{user-object},
...etc.
],
included['file--file'] = [
{file-object},
{file-object},
....etc.
],
I'm assuming this would require a custom module with some sort of hook where I could loop and sort the data before returning it to the iOS app. But, haven't been able to find any documentation on this type of json response modification.
Any help would be much appreciated.
Drupal implements the JSON:API specification. The specification specifies how a resource should be encoded. Both if it is returned as primary data or included as a relation resource.
If you want the data to be returned in another format, you would need to implement your own REST API in Drupal following another specification.
But if this is only about managing the data in a client, I would recommend to use one of the existing libraries for JSON:API specification. A list of implementations is provided at jsonapi.org/implementations/. The JSONAPI Swift package listed on that page seems to be well maintained.

Custom Slot wildcard value?

First off, I do not want to use AMAZON.Literal as it is for US only (I'm UK based) and I doubt it will be supported much longer.
I need a wildcard slot to allow users to say a place name (name of a shop for example), followed by the city.
City is easy, no problem.
The issue is the place name. I have a custom slot, but I can't list every shop in every city in the values.
I put a value of any in, which kind of works, but in my response, I'm only getting the last word if the user says a name that contains a few words e.g. Pound Land would just return Land.
Has anyone managed to do this?
As of 2018, you can use phrases to get user input that you may not be able to predefine.
{
"intents": [
{
"name": "SearchIntent",
"slots": [
{
"name": "Query",
"type": "AMAZON.SearchQuery"
},
{
"name": "CityList",
"type": "AMAZON.US_CITY"
}
],
"samples": [
"search for {Query} near me",
"find out {Query}",
"search for {Query}",
"give me details about {CityList}"
]
}
]
}
https://developer.amazon.com/blogs/alexa/post/a2716002-0f50-4587-b038-31ce631c0c07/enhance-speech-recognition-of-your-alexa-skills-with-phrase-slots-and-amazon-searchquery
When using custom slot types, AWS may return values from outside of the list yet it will try to map to the values. You can "hack" this behavior by providing a huge list of possible values. Maybe try to scrap a list of places and use that. I once tried with a list of 3000 landmarks and it was definitely returning slot values that were not in the list. The recognition was not great but I had an acoustic similarity function that allowed me to retrieve items from the list when needed. That was a while ago when they first talked about deprecating Amazon.LITERAL but eventually left it so I didn't have to worry about this.

Why use DELETE/POST instead of PUT for 'unfollowing/following' a user?

Referencing this API tutorial/explanation:
https://thinkster.io/tutorials/design-a-robust-json-api/getting-and-setting-user-data
The tutorial explains that to 'follow a user', you would use:
POST /api/profiles/:username/follow.
In order to 'unfollow a user', you would use:
DELETE /api/profiles/:username/follow.
The user Profile initially possesses the field "following": false.
I don't understand why the "following" field is being created/deleted (POST/DELETE) instead of updated from true to false. I feel as though I'm not grasping what's actually going on - are we not simply toggling the value of "following" between true and false?
Thanks!
I think that the database layer have to be implemented in a slightly more complex way than just having a boolean column for "following".
Given that you have three users, what would it mean that one of the users has "following": true? Is that user following something? That alone cannot mean that the user is following all other users, right?
The database layer probably consists of (at least) two different concepts: users and followings; users contain information about the user, and followings specify what users follow one another.
Say that we have two users:
[
{"username": "jake"},
{"username": "jane"}
]
And we want to say that Jane is following Jake, but not the other way around.
Then we need something to represent that concept. Let's call that a following:
{"follower": "jane", "followee": "jake"}
When the API talks about creating or deleting followings, this is probably what they imagine is getting created. That is why they use POST/DELETE instead of just PUT. They don't modify the user object, they create other objects that represent followings.
The reason they have a "following": true/false part in their JSON API response is because when you ask for information about a specific user, as one of the other users, you want to know if you as a user follows that specific user.
So, given the example above, when jane would ask for information about jake, at GET /api/profiles/jake, she would receive something like this:
{
"profile": {
"username": "jake",
"bio": "...",
"image": "...",
"following": true
}
}
However, when jake would ask for the profile information about jane, he would instead get this response:
{
"profile": {
"username": "jane",
"bio": "...",
"image": "...",
"following": false
}
}
So, the info they list as the API response is not what is actually stored in the database about this specific user, it also contains some information that is calculated based on who asked the question.
Using a microPUT would certainly be a reasonable alternative. I don't think anybody is going to be able to tell you why a random API tutorial made certain design decisions. It may be that they just needed a contrived example to use POST/DELETE.
Unless the author sees this question, I expect it's unanswerable. It's conceivable that they want to store meta information, such as the timestamp of the follow state change, but that would be unaffected by POST/DELETE vs. PUT.

Currently Using MySQL, Looking at DocumentDB

I currently use MySQL, after looking into Document DB it seems like it may be a good move. I do a TON (95%) of querying for single records. As my database gets larger, the time its taking to do this seems to be getting slower. Both reading and writing. I'm curious based on the (simplified) scheme below if it could be a good move to a DocumentDB, and what the layout would be for said schema (i'm a bit new to documentDB)
User
UserID
Username
CreatedDate
Tank
TankID
UserID REF User.UserID
TankName
Awards
Map
MapID
MapName
MapFIle
MapData
MapID REF Map.MapID
TankID REF Tank.TankID
Rank
Color
TimePlayed
Equipment
Everytime a player joins, the data from Tank,MapaData is Queried to gather a full tank object. Every time they die, win an award, kill somebody, or exit the game, the data is then written back out to tank,and mapdata.
The website queries the User table for login, which stores the username and a hash of the password. Once logged in the users are able to modify/delete/create new tanks on the website, which inserts records into the tank/mapdata tables.
The website also stores Top 25 in the World, t25 in map, t25 for each color, t25 for each color for each map.
That's about the only query patterns I can think of at this moment.
Based on the provided information you have the choice of several schema designs (with JSON as examples). I've made some assumptions, such as that more than one tank can be on one map and map data is only linked to a single map. You have to tweak it for your needs. I also try to provide some advantages and disadvantages of every solution.
Option #1 (Single collection)
This should be the easiest, but not the best solution. Here you put everything into one document with extreme "denormalization".
{
"mapname": "map1",
"mapfile": "mapfile1",
"data": {
"rank": "rank1",
"color": "color1",
...
"tanks": [
{
"name": "tank1",
...
"user": {
"name": "user1",
...
}
},
{
...
}
]
}
}
This solution works best when you do a lot of writes, rare updates and reads where you want to get all information together. On the other side it has a lot of disadvantages, such as storing user information directly into your application data (an example would be the password hash).
Option #2 (Two collections)
Put your user data into one collection and the other data into a second collection.
User collection
{
"id": 1,
"username": "user1",
"password": "passwordhash",
...
}
Data collection
{
"mapname": "map1",
"mapfile": "mapfile1",
"data": {
"rank": "rank1",
"color": "color1",
...
"tanks": [
{
"name": "tank1",
...
"user": userId
}
},
{
...
}
]
}
}
This option is a lot better than the first one. First you don't want to have sensitive user data (such as the hash of the password) in a collection with your other data. Also this works better for reads of the user object, because you just retrieve the information you need without skipping a lot of not needed fields. A disadvantage is that heavy write operations on the tank object can become a problem.
Option #3 (Three collections)
The next step could be to move the tanks out of the data collection into their own collection.
User collection
{
"id": 1,
"username": "user1",
"password": "passwordhash",
...
}
Tank collection
{
"name": "tank1",
...
"user": userId
}
Data collection
{
"mapname": "map1",
"mapfile": "mapfile1",
"data": {
"rank": "rank1",
"color": "color1",
...
"tanks": [
idOfTank1,
idOfTank2,
...
]
}
}
This works best for a lot of writes of single objects, such as the tanks, and reading tanks from their collection. This solution has its problems when reading a lot of data together, for example if you want to get a map and all the tanks in that map. In that case you have to resolve the dependencies of the tanks and the map data.
Summary
As seen, schema design is not easy in a document-oriented database. This is the reason why I asked for the query patterns. To come up with a good design you have to know most of the query patterns in advance. To get started, you should create a simple prototype with a design you think makes sense and test your query patterns with some test data. If that works, you can make minor changes to get even better performance. If not, rethink your query patterns and how a better design could look like. Keep in mind that you don't need a full-blown application for that. Most of that can be tested before a single line of code is written, for example with the administration shell of MongoDB or a simple console application in the case of DocumentDB.

freebase fields appear on web, but empty when using API

I am trying to get the notable_for field for this person, which appears on the web site
http://www.freebase.com/m/01z7_f
but this query returns an empty field
[{
"type": "/common/topic",
"notable_for": [],
"mid": "/m/01z7_f",
"name": null
}]
Am I using the right query here?
notable_for is a "synthetic property" which can't be queried via MQL. It is included in the Topic API though, so that's the thing to use if you need this property.
EDIT: One additional note -- the notable_for and notable_type properties are included in recent Freebase data dumps, so they can also be accessed that way:
$ zgrep ns:m.01z7_f freebase-rdf-2013-06-30-00-00.gz | grep notable
ns:m.01z7_f ns:common.topic.notable_for ns:g.1258t0lp5.
ns:m.01z7_f ns:common.topic.notable_types ns:m.01xrzmg.

Resources