documentation rc 3

This commit is contained in:
Theodor Diaconu 2017-12-01 12:58:36 +02:00
parent c88e2a9f36
commit ae989265cc
13 changed files with 82 additions and 48 deletions

View file

@ -2,6 +2,17 @@
Use this as a cheatsheet after you have read the full documentation.
- [Adding Links](#adding-links)
- [Adding Reducers](#adding-reducers)
- [Creating Named Queries](#creating-named-queries)
- [Exposing Named Queries](#exposing-named-queries)
- [Using Queries](#using-queries)
- [Caching Named Queries](#caching-named-queries)
- [Creating Global Queries](#creating-global-queries)
- [Exposing Global Queries](#exposing-global-queries)
### Adding Links
```js
@ -43,7 +54,7 @@ Collection.addReducers({
})
```
### Creating Named Query
### Creating Named Queries
```js
Collection.createQuery('queryName', {
@ -60,7 +71,7 @@ Collection.createQuery('queryName', {
})
```
### Exposing Named Query
### Exposing Named Queries
```js
query.expose({
@ -73,7 +84,7 @@ query.expose({
})
```
### Creating and Exposting Resolvers
### Creating and Exposing Resolvers
```js
// both
@ -90,7 +101,7 @@ query.resolve(function (params) {
});
```
### Using Query
### Using Queries
```js
query.setParams({}) // extends current params
@ -134,7 +145,8 @@ query.cacheResults(new MemoryResultCacher({
}))
```
#### Creating Global Query
#### Creating Global Queries
```js
Collection.createQuery({
$options, // Mongo Options {sort, limit, skip}
@ -147,7 +159,7 @@ Collection.createQuery({
})
```
#### Exposing Global Query
#### Exposing Global Queries
```js
Collection.expose({

View file

@ -5,7 +5,7 @@ Ok, it's time for Grapher to take it to the next level. Let's cache our very hea
Caching results only works for Named Queries.
Let's take an example
Let's take an example:
```js
export default Meteor.users.createQuery('myFriendsEmails', {
@ -69,6 +69,7 @@ you may want to use `redis` for that.
```js
import {BaseResultCacher} from 'meteor/cultofcoders:grapher';
import {EJSON} from 'meteor/ejson';
/**
* Redis Cacher
@ -115,7 +116,7 @@ myFriendsQuery.cacheResults(cacher);
## Caching Resolver Queries
As you may have guessed, this works with resolver queries as well, in their case, instead of the actual query,
As you may have guessed, this works with [resolver queries](named_queries.md#resolvers) as well, in their case, instead of the actual query,
we pass as `query` parameter an interface containing `fetch()`.
And your cacher, will never receive `cursorCount` inside the `fetchables` object in this case.
@ -127,12 +128,12 @@ Therefore you can use the same paradigms for your cache for resolver queries as
Unfortunately, if you want to invalidate your cache you can do it yourself manually, as this is not implemented,
but since you can hook in your own cacher, you can do whatever you want.
## [Conclusion]
## Conclusion
If you have very heavy and frequent requests to the database, or any type of requests (resolver queries) you can think
about using a cache, with very few lines of code.
#### [Continue Reading](global_exposure.md) or [Back to Table of Contents](table_of_contents.md)
## [Continue Reading](global_exposure.md) or [Back to Table of Contents](index.md)

View file

@ -11,7 +11,7 @@ You may need denormalization in such cases where:
A simple example, a user has an avatar that is stored in the image collection:
```js
// Assuming Images of schema: {_id, path, smallThumb, createdAt}
// Assuming Images of schema: {_id, path, smallThumbPath, createdAt}
import Images from '...';
Meteor.users.addLinks({
@ -24,7 +24,7 @@ Meteor.users.addLinks({
// what fields to cache from Images, this only works for fields and not links
body: {
path: 1,
smallThumb: 1,
smallThumbPath: 1,
}
}
}
@ -83,14 +83,14 @@ Denormalization works with any type of links `one`, `many`, `meta` whether they
We previously tackled the case where we needed `$postFilters` or `$postFilter` to retrieve filtered data.
For example, let's say we want to retrieve only the users that have reviewed a book of a certain type,
and inside `users` collection we have a `reviewedBoos` link.
and inside `users` collection we have a `reviewedBooks` link.
```js
Meteor.users.addLinks({
'reviewedBooks': {
type: 'many',
collection: Books,
field: 'reviewedBooksIds',
field: 'reviewedBookIds',
denormalize: {
body: {
type: 1,
@ -142,12 +142,13 @@ If you want to use deep filters, it will not work with denormalized cache, you c
}
```
Because if you put `$filters: {}` inside the body of the cache, it will regard it as a foreign field, and it will fetch the linked Collection for it.
Because if you put `$filters: {}` inside the body of the cache, it will regard it as a foreign field, and it will
fetch the linked Collection for it, falling back to the original behavior.
A current limitation for denormalized meta links, is that we will no longer be able to store the `$metadata` inside the nested object, because that
would require additional fetching of the link storage if we are querying the graph from the inversed side.
## [Conclusion]
## Conclusion
Using denormalization can enable you to do wonderful things inside NoSQL, but also be careful because they come with a cost,
that may not be very noticeable in the beginning. But can also dramatically improve performance at the same time.
@ -155,4 +156,4 @@ that may not be very noticeable in the beginning. But can also dramatically impr
I suggest that they should be used to cache things that rarely change such as an user's avatar, or when you need to do
powerful and frequent searches, that otherwise would have consumed more resources.
#### [Continue Reading](caching_results.md) or [Back to Table of Contents](table_of_contents.md)
## [Continue Reading](caching_results.md) or [Back to Table of Contents](index.md)

View file

@ -335,9 +335,9 @@ This will allow requests like:
}
```
## [Conclusion]
## Conclusion
The global queries are a very powerful tool to expose your full database, but unlike `Named Queries` they do
not benefit of `caching`.
#### [Continue Reading](structure_and_patterns.md) or [Back to Table of Contents](table_of_contents.md)
## [Continue Reading](structure_and_patterns.md) or [Back to Table of Contents](index.md)

View file

@ -73,8 +73,9 @@ propper objects.
Not only it makes 5 requests instead of 131, but it smartly re-uses categories and authors at each collection node,
meaning you will have less bandwidth consumed.
Making it more efficient in terms of bandwidth than SQL. Yes, you read that correct:
Making it more efficient in terms of bandwidth than SQL or other relational databases. Yes, you read that correct.
Example:
```js
{
posts: {
@ -87,13 +88,13 @@ Making it more efficient in terms of bandwidth than SQL. Yes, you read that corr
Let's assume we have 100 posts, and the total number of categories is like 4. Hypernova does 2 requests to the database,
and fetches 100 posts, and 4 categories. If you would have used `JOIN` functionality in SQL, you would have received
categories for each post.
the categories for each post.
Now you understand why this is a revolution for MongoDB and Meteor.
Keep in mind that Hypernova is only used for static queries. For reactive queries, we still rely on the recursive fetching.
#### [Continue Reading](denormalization.md) or [Back to Table of Contents](table_of_contents.md)
## [Continue Reading](denormalization.md) or [Back to Table of Contents](index.md)

View file

@ -208,9 +208,9 @@ export default Posts.createQuery({...}, {
});
```
## [Conclusion]
## Conclusion
This is the end of our introduction. As we can see, we can make queries modular and this already gives us
a big benefit. By abstracting them into their own modules we can keep our methods neat and clean.
#### [Continue Reading](linking_collections.md) or [Back to Table of Contents](table_of_contents.md)
## [Continue Reading](linking_collections.md) or [Back to Table of Contents](index.md)

View file

@ -162,12 +162,12 @@ userGroupsLink.metadata([groupId1, groupId2], {
Updating metadata only works with strings or objects that contain `_id`, and it works from both sides.
## [Conclusion]
## Conclusion
By using this Programatic API to set your links instead of relying on updates, it makes your code much simpler to read,
and makes schema migration easier in the future.
#### [Continue Reading](query_options.md) or [Back to Table of Contents](table_of_contents.md)
## [Continue Reading](query_options.md) or [Back to Table of Contents](index.md)

View file

@ -450,12 +450,12 @@ eliminate large and complex documents by abstracting them into collections.
In the future, this limitation may change, but for now you can work around this and keep your code elegant.
## [Conclusion]
## Conclusion
Using these simple techniques, you can create a beautiful database schemas inside MongoDB that are relational and very simple to fetch,
you will eliminate almost all your boilerplate code around this and allows you to focus on more important things.
#### [Continue Reading](linker_engine.md) or [Back to Table of Contents](table_of_contents.md)
## [Continue Reading](linker_engine.md) or [Back to Table of Contents](index.md)

View file

@ -391,10 +391,10 @@ query.expose({
})
```
## [Conclusion]
## Conclusion
We can now safely expose our queries to the client, and the client can use it in a simple and uniform way.
By this stage we already understand how powerful Grapher really is, but it still has some tricks up it's sleeve.
#### [Continue Reading](hypernova.md) or [Back to Table of Contents](table_of_contents.md)
## [Continue Reading](hypernova.md) or [Back to Table of Contents](index.md)

View file

@ -2,7 +2,7 @@
If you like Grapher, or if `like` is an understatement, you can use it in any other language/medium.
You can expose it as an HTTP API, or a DDP API (as a Meteor method) for example, because
You can expose it as an `HTTP API`, or via `DDP` (as a meteor method) for example, because
in React Native you have ways to connect to Meteor with:
https://www.npmjs.com/package/react-native-meteor#meteor-collections
@ -52,9 +52,10 @@ grapherRoutes.route('/grapher', function (req, res) {
try {
const data = actualQuery.fetch({
// the userId (User Identification) that hits the firewalls
// user id can be anything, an API key maybe, not only a 'string'
// it's up to you and your firewalls to decide
userId: 'XXX'
// user id can be anything, an API KEY, an Object, you and your firewalls decide
userId: 'XXX',
// you can specify other fields, because this is the context of the firewall
// whatever you define here, it can be accessed with `this`
});
res.statusCode = 200;
@ -75,7 +76,8 @@ in EJSON format. If `EJSON` is not available in your language, you can parse it
If you are in the JavaScript world: https://www.npmjs.com/package/ejson
If you want to use Meteor's Methods as an HTTP API to also handle method calls, take a look here: https://github.com/cult-of-coders/fusion
If you want to use Meteor's Methods as an `HTTP API` to also handle mutations,
take a look here: https://github.com/cult-of-coders/fusion
And more closely: https://github.com/cult-of-coders/fusion/blob/7ec5cd50c3a471c0bdd65c9fa482124c149dc243/fusion/server/route.js
@ -107,8 +109,8 @@ Meteor.methods({
})
```
## [Conclusion]
## Conclusion
Nothing stops you from using Grapher outside Meteor!
#### [Continue Reading](api.md) or [Back to Table of Contents](table_of_contents.md)
## [Continue Reading](api.md) or [Back to Table of Contents](index.md)

View file

@ -368,12 +368,12 @@ const query = postLists.clone({
When we cross to the client-side domain we need to be very wary of these type of injections.
## [Conclusion]
## Conclusion
Query is a very powerful tool, very flexible, it allows us to do very complex things that would have taken us a lot of time
to do otherwise.
#### [Continue Reading](reducers.md) or [Back to Table of Contents](table_of_contents.md)
## [Continue Reading](reducers.md) or [Back to Table of Contents](index.md)

View file

@ -190,9 +190,9 @@ Projects.addReducers({
If you want to filter reducers you can use `$postFilters` or `$postFilter` special functions.
## [Conclusion]
## Conclusion
Reducers are a neat way to remove boilerplate from your code, especially for our infamous `emails[0].address`,
inside `Meteor.users` collection, check if you can figure out how to reduce it!
#### [Continue Reading](named_queries.md) or [Back to Table of Contents](table_of_contents.md)
## [Continue Reading](named_queries.md) or [Back to Table of Contents](index.md)

View file

@ -21,14 +21,16 @@ We suggest you read: http://www.meteor-tuts.com/chapters/3/persistence-layer.htm
If you respect the patterns above you will avoid having the most common pitfalls with Grapher:
**Reducer/Links not working?**
Make sure they are imported in the environment you use them client/server.
**My link is not saved in the database**
**My link is not saved in the database?**
Make sure you added it correctly to your SimpleSchema, if you have that.
## Fragments
You will find yourself requiring often same fields for Users, such as email, fullName, and maybe avatar.
You will find yourself requiring often same fields for users, such as `email`, `fullName`, and maybe `avatar`.
For that let's create some fragments:
```js
@ -63,7 +65,8 @@ Invoices.createQuery({
You can also compose certain fragments:
```js
import compose from 'meteor/cultofcoders:grapher';
import {compose} from 'meteor/cultofcoders:grapher';
import {
UserPublicFragment,
UserBillingFragment,
@ -81,9 +84,9 @@ Invoices.createQuery({
})
```
Compose uses a deep extension, so it works how you expected to work, especially if some fragments have shared bodies.
`compose()` uses deep extension, so it works how you expect it to work, especially if some fragments have shared bodies.
Do not use special properties inside Fragments, such as `$filters`, `$options`, etc.
Do not use special properties inside fragments, such as `$filters`, `$options`, etc.
## Scaling Reactivity
@ -104,8 +107,22 @@ export default Messages.createQuery('messagesForThread', {
})
```
## [Conclusion]
Or, if you don't want to expose that, embody the `$filter()` server-side:
```js
// query.expose.js
query.expose({
embody: {
$filter({options, filters, params}) {
filters.threadId = params.threadId;
options.namespace = `thread::${params.threadId}`;
}
}
})
```
## Conclusion
Using some simple techniques we can make our code much easier to read, and we can make use of a scalable data graph using `redis-oplog`
#### [Continue Reading](outside_meteor.md) or [Back to Table of Contents](table_of_contents.md)
## [Continue Reading](outside_meteor.md) or [Back to Table of Contents](index.md)