mirror of
https://github.com/vale981/grapher
synced 2025-03-05 09:31:42 -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({
|
||||
// $all: 1, // use this only when you want all fields without specifying them (NOT RECOMMENDED)
|
||||
$filter({filters, options, params}) {
|
||||
filters.isApproved = true;
|
||||
options.limit = params.limit;
|
||||
|
|
|
@ -99,7 +99,6 @@ Notes:
|
|||
- Use {} to specify a link, and 1 for a field.
|
||||
- "_id" will always be fetched
|
||||
- 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({
|
||||
|
@ -116,7 +115,6 @@ const query = Posts.createQuery({
|
|||
comments: {
|
||||
text: 1,
|
||||
// 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.
|
||||
author: {
|
||||
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)
|
||||
|
||||
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
|
||||
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 firewall = this.firewall;
|
||||
|
||||
collection.__isExposedForGrapher = true;
|
||||
|
||||
if (firewall) {
|
||||
collection.firewall = (filters, options, userId) => {
|
||||
if (userId !== undefined) {
|
||||
|
|
|
@ -100,6 +100,10 @@ export default class Linker {
|
|||
*/
|
||||
isMeta()
|
||||
{
|
||||
if (this.isVirtual()) {
|
||||
return this.linkConfig.relatedLinker.isMeta();
|
||||
}
|
||||
|
||||
return !!this.linkConfig.metadata;
|
||||
}
|
||||
|
||||
|
@ -108,6 +112,10 @@ export default class Linker {
|
|||
*/
|
||||
isSingle()
|
||||
{
|
||||
if (this.isVirtual()) {
|
||||
return this.linkConfig.relatedLinker.isSingle();
|
||||
}
|
||||
|
||||
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 isVirtual = linker.isVirtual();
|
||||
const isSingle = linker.isSingle();
|
||||
const oneResult = (isVirtual && linker.linkConfig.relatedLinker.linkConfig.unique)
|
||||
|| (!isVirtual) && isSingle;
|
||||
const removeStorageField = !childCollectionNode.parentHasMyLinkStorageFieldSpecified();
|
||||
const oneResult = (isVirtual && linker.linkConfig.relatedLinker.linkConfig.unique) || (!isVirtual) && isSingle;
|
||||
|
||||
const fieldStorage = linker.linkStorageField;
|
||||
|
||||
_.each(parent.results, result => {
|
||||
result[childCollectionNode.linkName] = assembleData(childCollectionNode, result, {
|
||||
fieldStorage, strategy, isVirtual, isSingle, oneResult, limit
|
||||
const data = assembleData(childCollectionNode, result, {
|
||||
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}) {
|
||||
const filters = createSearchFilters(result, fieldStorage, strategy, isVirtual);
|
||||
const data = sift(filters, childCollectionNode.results);
|
||||
|
||||
function filterAssembledData(data, {limit, oneResult}) {
|
||||
if (limit) {
|
||||
return data.slice(limit);
|
||||
}
|
||||
|
@ -34,3 +37,9 @@ function assembleData(childCollectionNode, result, {fieldStorage, strategy, isVi
|
|||
|
||||
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 AggregateFilters from './aggregateSearchFilters.js';
|
||||
import assemble from './assembler.js';
|
||||
import assembleAggregateResults from './assembleAggregateResults.js';
|
||||
import buildAggregatePipeline from './buildAggregatePipeline.js';
|
||||
|
||||
export default function storeHypernovaResults(childCollectionNode, userId) {
|
||||
if (childCollectionNode.parent.results.length === 0) {
|
||||
|
@ -26,102 +28,13 @@ export default function storeHypernovaResults(childCollectionNode, userId) {
|
|||
childCollectionNode.results = collection.find(filters, filteredOptions, userId).fetch();
|
||||
}
|
||||
|
||||
assemble(childCollectionNode);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
assemble(childCollectionNode, options.limit);
|
||||
} else {
|
||||
// virtuals arrive here
|
||||
let pipeline = [];
|
||||
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}
|
||||
}
|
||||
})
|
||||
}
|
||||
let pipeline = buildAggregatePipeline(childCollectionNode, filters, options, userId);
|
||||
|
||||
const aggregateResults = collection.aggregate(pipeline, {explains: true});
|
||||
|
||||
let results = [];
|
||||
|
||||
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));
|
||||
assembleAggregateResults(childCollectionNode, aggregateResults);
|
||||
}
|
||||
});
|
||||
|
||||
_.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);
|
||||
}
|
||||
|
||||
hasGlobalFieldNode() {
|
||||
return !!_.find(this.fieldNodes, n => n.isGlobal());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param node
|
||||
* @param linker
|
||||
|
@ -41,10 +37,6 @@ export default class CollectionNode {
|
|||
applyFields(filters, options) {
|
||||
let hasAddedAnyField = false;
|
||||
|
||||
if (this.hasGlobalFieldNode()) {
|
||||
return options.fields = undefined;
|
||||
}
|
||||
|
||||
_.each(this.fieldNodes, n => {
|
||||
hasAddedAnyField = true;
|
||||
n.applyFields(options.fields)
|
||||
|
@ -70,4 +62,16 @@ export default class CollectionNode {
|
|||
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 {
|
||||
constructor(name, body) {
|
||||
this.name = name;
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
isGlobal() {
|
||||
return this.name === '$all';
|
||||
this.body = _.isObject(body) ? 1 : body;
|
||||
}
|
||||
|
||||
applyFields(fields) {
|
||||
|
|
Loading…
Add table
Reference in a new issue