mirror of
https://github.com/vale981/grapher
synced 2025-03-05 17:41:41 -05:00
consistency fixes, updated readme, removed link storage if not specified
This commit is contained in:
parent
6aa1819f48
commit
e844c93548
10 changed files with 142 additions and 135 deletions
|
@ -109,7 +109,6 @@ OR from the collection directly:
|
||||||
|
|
||||||
```
|
```
|
||||||
const query = Posts.createQuery({
|
const query = Posts.createQuery({
|
||||||
// $all: 1, // use this only when you want all fields without specifying them (NOT RECOMMENDED)
|
|
||||||
$filter({filters, options, params}) {
|
$filter({filters, options, params}) {
|
||||||
filters.isApproved = true;
|
filters.isApproved = true;
|
||||||
options.limit = params.limit;
|
options.limit = params.limit;
|
||||||
|
|
|
@ -99,7 +99,6 @@ Notes:
|
||||||
- Use {} to specify a link, and 1 for a field.
|
- Use {} to specify a link, and 1 for a field.
|
||||||
- "_id" will always be fetched
|
- "_id" will always be fetched
|
||||||
- You must always specify the fields you need, otherwise it will only fetch _id
|
- You must always specify the fields you need, otherwise it will only fetch _id
|
||||||
- If you want all fields, pass in {$all: 1}
|
|
||||||
|
|
||||||
```
|
```
|
||||||
const query = Posts.createQuery({
|
const query = Posts.createQuery({
|
||||||
|
@ -116,7 +115,6 @@ const query = Posts.createQuery({
|
||||||
comments: {
|
comments: {
|
||||||
text: 1,
|
text: 1,
|
||||||
// if you don't specify any local fields for the author, only "_id" field will be fetched
|
// if you don't specify any local fields for the author, only "_id" field will be fetched
|
||||||
// use $all: 1, to get all fields
|
|
||||||
// this will enforce the use of query and retrieve only the data you need.
|
// this will enforce the use of query and retrieve only the data you need.
|
||||||
author: {
|
author: {
|
||||||
groups: {
|
groups: {
|
||||||
|
@ -283,23 +281,6 @@ createQuery({
|
||||||
|
|
||||||
*posts* is the name of the collection. (when you create new Mongo.Collection("xxx"), "xxx" is the name of your collection)
|
*posts* is the name of the collection. (when you create new Mongo.Collection("xxx"), "xxx" is the name of your collection)
|
||||||
|
|
||||||
Getting all the fields
|
|
||||||
======================
|
|
||||||
|
|
||||||
Though this is not recommended, sometimes especially when you are just testing around, you want to see all the fields
|
|
||||||
```
|
|
||||||
createQuery({
|
|
||||||
posts: {
|
|
||||||
$all: 1,
|
|
||||||
comments: {
|
|
||||||
$all: 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
The query above will fetch all the fields from every posts and every comment of every post.
|
|
||||||
|
|
||||||
#### React Integration
|
#### React Integration
|
||||||
For integration with React try out [cultofcoders:grapher-react](https://github.com/cult-of-coders/grapher-react) package
|
For integration with React try out [cultofcoders:grapher-react](https://github.com/cult-of-coders/grapher-react) package
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,8 @@ export default class Exposure {
|
||||||
const collection = this.collection;
|
const collection = this.collection;
|
||||||
const firewall = this.firewall;
|
const firewall = this.firewall;
|
||||||
|
|
||||||
|
collection.__isExposedForGrapher = true;
|
||||||
|
|
||||||
if (firewall) {
|
if (firewall) {
|
||||||
collection.firewall = (filters, options, userId) => {
|
collection.firewall = (filters, options, userId) => {
|
||||||
if (userId !== undefined) {
|
if (userId !== undefined) {
|
||||||
|
|
|
@ -100,6 +100,10 @@ export default class Linker {
|
||||||
*/
|
*/
|
||||||
isMeta()
|
isMeta()
|
||||||
{
|
{
|
||||||
|
if (this.isVirtual()) {
|
||||||
|
return this.linkConfig.relatedLinker.isMeta();
|
||||||
|
}
|
||||||
|
|
||||||
return !!this.linkConfig.metadata;
|
return !!this.linkConfig.metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,6 +112,10 @@ export default class Linker {
|
||||||
*/
|
*/
|
||||||
isSingle()
|
isSingle()
|
||||||
{
|
{
|
||||||
|
if (this.isVirtual()) {
|
||||||
|
return this.linkConfig.relatedLinker.isSingle();
|
||||||
|
}
|
||||||
|
|
||||||
return _.contains(this.oneTypes, this.linkConfig.type);
|
return _.contains(this.oneTypes, this.linkConfig.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
40
lib/query/hypernova/assembleAggregateResults.js
Normal file
40
lib/query/hypernova/assembleAggregateResults.js
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
export default function (childCollectionNode, aggregateResults) {
|
||||||
|
const linker = childCollectionNode.linker;
|
||||||
|
const linkName = childCollectionNode.linkName;
|
||||||
|
|
||||||
|
let allResults = [];
|
||||||
|
|
||||||
|
if (linker.isMeta() && linker.isMany()) {
|
||||||
|
_.each(childCollectionNode.parent.results, parentResult => {
|
||||||
|
parentResult[linkName] = parentResult[linkName] || [];
|
||||||
|
|
||||||
|
const eligibleAggregateResults = _.filter(aggregateResults, aggregateResult => {
|
||||||
|
return _.contains(aggregateResult._id, parentResult._id)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (eligibleAggregateResults.length) {
|
||||||
|
const datas = _.pluck(eligibleAggregateResults, 'data'); /// [ [x1, x2], [x2, x3] ]
|
||||||
|
|
||||||
|
_.each(datas, item => parentResult[linkName].push(item));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_.each(aggregateResults, aggregateResult => {
|
||||||
|
_.each(aggregateResult.data, item => allResults.push(item))
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
_.each(aggregateResults, aggregateResult => {
|
||||||
|
const parentResult = _.find(childCollectionNode.parent.results, (result) => {
|
||||||
|
return result._id === aggregateResult._id;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (parentResult) {
|
||||||
|
parentResult[childCollectionNode.linkName] = aggregateResult.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
_.each(aggregateResult.data, item => allResults.push(item))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
childCollectionNode.results = allResults;
|
||||||
|
}
|
|
@ -8,22 +8,25 @@ export default (childCollectionNode, limit) => {
|
||||||
const strategy = linker.strategy;
|
const strategy = linker.strategy;
|
||||||
const isVirtual = linker.isVirtual();
|
const isVirtual = linker.isVirtual();
|
||||||
const isSingle = linker.isSingle();
|
const isSingle = linker.isSingle();
|
||||||
const oneResult = (isVirtual && linker.linkConfig.relatedLinker.linkConfig.unique)
|
const removeStorageField = !childCollectionNode.parentHasMyLinkStorageFieldSpecified();
|
||||||
|| (!isVirtual) && isSingle;
|
const oneResult = (isVirtual && linker.linkConfig.relatedLinker.linkConfig.unique) || (!isVirtual) && isSingle;
|
||||||
|
|
||||||
const fieldStorage = linker.linkStorageField;
|
const fieldStorage = linker.linkStorageField;
|
||||||
|
|
||||||
_.each(parent.results, result => {
|
_.each(parent.results, result => {
|
||||||
result[childCollectionNode.linkName] = assembleData(childCollectionNode, result, {
|
const data = assembleData(childCollectionNode, result, {
|
||||||
fieldStorage, strategy, isVirtual, isSingle, oneResult, limit
|
fieldStorage, strategy, isVirtual, isSingle
|
||||||
});
|
});
|
||||||
|
|
||||||
|
result[childCollectionNode.linkName] = filterAssembledData(data, {limit, oneResult})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (removeStorageField) {
|
||||||
|
_.each(parent.results, result => delete result[fieldStorage]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function assembleData(childCollectionNode, result, {fieldStorage, strategy, isVirtual, oneResult, limit}) {
|
function filterAssembledData(data, {limit, oneResult}) {
|
||||||
const filters = createSearchFilters(result, fieldStorage, strategy, isVirtual);
|
|
||||||
const data = sift(filters, childCollectionNode.results);
|
|
||||||
|
|
||||||
if (limit) {
|
if (limit) {
|
||||||
return data.slice(limit);
|
return data.slice(limit);
|
||||||
}
|
}
|
||||||
|
@ -34,3 +37,9 @@ function assembleData(childCollectionNode, result, {fieldStorage, strategy, isVi
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function assembleData(childCollectionNode, result, {fieldStorage, strategy, isVirtual}) {
|
||||||
|
const filters = createSearchFilters(result, fieldStorage, strategy, isVirtual);
|
||||||
|
|
||||||
|
return sift(filters, childCollectionNode.results);
|
||||||
|
}
|
55
lib/query/hypernova/buildAggregatePipeline.js
Normal file
55
lib/query/hypernova/buildAggregatePipeline.js
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
export default function (childCollectionNode, filters, options, userId) {
|
||||||
|
const linker = childCollectionNode.linker;
|
||||||
|
const linkStorageField = linker.linkStorageField;
|
||||||
|
const collection = childCollectionNode.collection;
|
||||||
|
|
||||||
|
let pipeline = [];
|
||||||
|
|
||||||
|
if (collection.firewall) {
|
||||||
|
collection.firewall(filters, options, userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeline.push({$match: filters});
|
||||||
|
|
||||||
|
if (options.sort) {
|
||||||
|
pipeline.push({$sort: options.sort})
|
||||||
|
}
|
||||||
|
|
||||||
|
let _id = linkStorageField;
|
||||||
|
if (linker.isMeta()) {
|
||||||
|
_id += '._id';
|
||||||
|
}
|
||||||
|
|
||||||
|
let dataPush = {};
|
||||||
|
_.each(options.fields, (value, field) => {
|
||||||
|
dataPush[field] = '$' + field
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!dataPush._id) {
|
||||||
|
dataPush['_id'] = '$_id';
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeline.push({
|
||||||
|
$group: {
|
||||||
|
_id: "$" + _id,
|
||||||
|
data: {
|
||||||
|
$push: dataPush
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (options.limit || options.skip) {
|
||||||
|
let $slice = ["$data"];
|
||||||
|
if (options.skip) $slice.push(options.skip);
|
||||||
|
if (options.limit) $slice.push(options.limit);
|
||||||
|
|
||||||
|
pipeline.push({
|
||||||
|
$project: {
|
||||||
|
_id: 1,
|
||||||
|
data: {$slice}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return pipeline;
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
import applyProps from '../lib/applyProps.js';
|
import applyProps from '../lib/applyProps.js';
|
||||||
import AggregateFilters from './aggregateSearchFilters.js';
|
import AggregateFilters from './aggregateSearchFilters.js';
|
||||||
import assemble from './assembler.js';
|
import assemble from './assembler.js';
|
||||||
|
import assembleAggregateResults from './assembleAggregateResults.js';
|
||||||
|
import buildAggregatePipeline from './buildAggregatePipeline.js';
|
||||||
|
|
||||||
export default function storeHypernovaResults(childCollectionNode, userId) {
|
export default function storeHypernovaResults(childCollectionNode, userId) {
|
||||||
if (childCollectionNode.parent.results.length === 0) {
|
if (childCollectionNode.parent.results.length === 0) {
|
||||||
|
@ -26,102 +28,13 @@ export default function storeHypernovaResults(childCollectionNode, userId) {
|
||||||
childCollectionNode.results = collection.find(filters, filteredOptions, userId).fetch();
|
childCollectionNode.results = collection.find(filters, filteredOptions, userId).fetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
assemble(childCollectionNode);
|
assemble(childCollectionNode, options.limit);
|
||||||
|
} else {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// virtuals arrive here
|
// virtuals arrive here
|
||||||
let pipeline = [];
|
let pipeline = buildAggregatePipeline(childCollectionNode, filters, options, userId);
|
||||||
const linkStorageField = aggregateFilters.linkStorageField;
|
|
||||||
|
|
||||||
if (collection.firewall) {
|
|
||||||
collection.firewall(filters, options, userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
pipeline.push({$match: filters});
|
|
||||||
|
|
||||||
if (options.sort) {
|
|
||||||
pipeline.push({$sort: options.sort})
|
|
||||||
}
|
|
||||||
|
|
||||||
let _id = linkStorageField;
|
|
||||||
if (linker.isMeta()) {
|
|
||||||
_id += '._id';
|
|
||||||
}
|
|
||||||
|
|
||||||
let dataPush = {};
|
|
||||||
_.each(options.fields, (value, field) => {
|
|
||||||
dataPush[field] = '$' + field
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!dataPush._id) {
|
|
||||||
dataPush['_id'] = '$_id';
|
|
||||||
}
|
|
||||||
|
|
||||||
dataPush[linkStorageField] = '$' + linkStorageField;
|
|
||||||
|
|
||||||
|
|
||||||
pipeline.push({
|
|
||||||
$group: {
|
|
||||||
_id: "$" + _id,
|
|
||||||
data: {
|
|
||||||
$push: dataPush
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (options.limit || options.skip) {
|
|
||||||
let $slice = ["$data"];
|
|
||||||
if (options.skip) $slice.push(options.skip);
|
|
||||||
if (options.limit) $slice.push(options.limit);
|
|
||||||
|
|
||||||
pipeline.push({
|
|
||||||
$project: {
|
|
||||||
_id: 1,
|
|
||||||
data: {$slice}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const aggregateResults = collection.aggregate(pipeline, {explains: true});
|
const aggregateResults = collection.aggregate(pipeline, {explains: true});
|
||||||
|
|
||||||
let results = [];
|
assembleAggregateResults(childCollectionNode, aggregateResults);
|
||||||
|
|
||||||
const linkName = childCollectionNode.linkName;
|
|
||||||
|
|
||||||
if (linker.isMany()) {
|
|
||||||
_.each(childCollectionNode.parent.results, parentResult => {
|
|
||||||
parentResult[linkName] = parentResult[linkName] || [];
|
|
||||||
|
|
||||||
const eligibleAggregateResults = _.filter(aggregateResults, aggregateResult => {
|
|
||||||
return _.contains(aggregateResult._id, parentResult._id)
|
|
||||||
});
|
|
||||||
|
|
||||||
if (eligibleAggregateResults.length) {
|
|
||||||
const datas = _.pluck(eligibleAggregateResults, 'data'); /// [ [x1, x2], [x2, x3] ]
|
|
||||||
|
|
||||||
_.each(datas, item => parentResult[linkName].push(item));
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
_.each(aggregateResults, aggregateResult => {
|
|
||||||
_.each(aggregateResult.data, item => results.push(item))
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
_.each(aggregateResults, aggregateResult => {
|
|
||||||
const parentResult = _.find(childCollectionNode.parent.results, (result) => {
|
|
||||||
return result._id === aggregateResult._id;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (parentResult) {
|
|
||||||
parentResult[childCollectionNode.linkName] = aggregateResult.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
_.each(aggregateResult.data, item => results.push(item))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
childCollectionNode.results = results;
|
|
||||||
}
|
}
|
|
@ -19,10 +19,6 @@ export default class CollectionNode {
|
||||||
return _.filter(this.nodes, n => n instanceof FieldNode);
|
return _.filter(this.nodes, n => n instanceof FieldNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
hasGlobalFieldNode() {
|
|
||||||
return !!_.find(this.fieldNodes, n => n.isGlobal());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param node
|
* @param node
|
||||||
* @param linker
|
* @param linker
|
||||||
|
@ -41,10 +37,6 @@ export default class CollectionNode {
|
||||||
applyFields(filters, options) {
|
applyFields(filters, options) {
|
||||||
let hasAddedAnyField = false;
|
let hasAddedAnyField = false;
|
||||||
|
|
||||||
if (this.hasGlobalFieldNode()) {
|
|
||||||
return options.fields = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
_.each(this.fieldNodes, n => {
|
_.each(this.fieldNodes, n => {
|
||||||
hasAddedAnyField = true;
|
hasAddedAnyField = true;
|
||||||
n.applyFields(options.fields)
|
n.applyFields(options.fields)
|
||||||
|
@ -70,4 +62,16 @@ export default class CollectionNode {
|
||||||
options.fields = {_id: 1};
|
options.fields = {_id: 1};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parentHasMyLinkStorageFieldSpecified() {
|
||||||
|
const storageField = this.linker.linkStorageField;
|
||||||
|
|
||||||
|
if (this.parent) {
|
||||||
|
return !!_.find(this.parent.fieldNodes, fieldNode => {
|
||||||
|
return fieldNode.name == storageField
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
export default class FieldNode {
|
export default class FieldNode {
|
||||||
constructor(name, body) {
|
constructor(name, body) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.body = body;
|
this.body = _.isObject(body) ? 1 : body;
|
||||||
}
|
|
||||||
|
|
||||||
isGlobal() {
|
|
||||||
return this.name === '$all';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
applyFields(fields) {
|
applyFields(fields) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue