consistency fixes, updated readme, removed link storage if not specified

This commit is contained in:
Theodor Diaconu 2016-09-22 17:06:05 +03:00
parent 6aa1819f48
commit e844c93548
10 changed files with 142 additions and 135 deletions

View file

@ -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;

View file

@ -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

View file

@ -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) {

View file

@ -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);
} }

View 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;
}

View file

@ -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);
}

View 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;
}

View file

@ -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;
} }

View file

@ -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;
}
} }

View file

@ -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) {