The lookback api docs say PortfolioItem field is an index. Is the lowest portfolio item type also an index?
E.g: the Portfolio Item types in my workspace are Product, Milestone and Feature. will Feature be an index in Lookback API in addition to PortfolioItem?
The reason I ask is because only top-level UserStories have the PortfolioItem field, but both top-level and child UserStories have the Feature field. I want to query all User Stories under a particular Feature, which means I can't use PortfolioItem field, because it will not include child User Stories, only top-level User Stories.
Example of what i want to do if Feature is indexed:
Ext.create('Rally.data.lookback.SnapshotStore', {
listeners: {
load: function(store, data, success) {
//do stuff
}
},
autoLoad:true,
limit:Infinity,
fetch: ['ScheduleState', 'PlanEstimate', 'Feature', 'ObjectID'],
compress:true,
find: {
_TypeHierarchy: 'HierarchicalRequirement',
Children: null,
Release: //a release OID
},
hydrate: ['ScheduleState']
});
There may be some confusion with the use of the word 'index'. Some fields are "indexed" for fast lookup..."Feature" isn't one of them, though it is a valid field and you can search for it. More correctly, the field that is the lowest-level Portfolio Item type is kept in the snapshots.* Given what you're asking for, adding "Feature": {oid} to the find should give you what you want.
* The distinction is due to the fact that the label "Feature" can be changed to something else, so what is "Feature" in one workspace might be "Thing" in another.
The _ItemHierarchy field includes all levels of PortfolioItems through all levels of Stories to Defects, Tasks and (I'm pretty sure) TestCases. So, if you want "all User Stories under a particular Feature", simply specify find: {_ItemHierarchy: 1234567} where 1234567 is the ObjectID of the Feature. You could combine this with the _TypeHierarchy and Release clauses. If you combine it with the Children and _TypeHiearchy clauses as you proposes, that will give you only leaf stories as opposed to all the levels. This is ideal if you are doing aggregations on fields like sum of PlanEstimate (points) or TaskActual, etc.
Note, I don't think this has anything to do with being indexed, so I may be misunderstanding your question. Please accept my apology if that's the case.
Related
I have a "products" collection:
products:{
PD1232:{
name:"Brick",
type: LPK52
},
PD553: {
name:"Panel",
type: DX343
}
}
The products collection is referenced to a "lookup" collection:
productTypes:{
LPK52:{
name: "Concrete mixed with dried bamboo"
},
DX343: {
name: "bla bla........"
}
}
In terms of searching the products, I want to index the "products" collection as a whole. It means I want to also combine a collection and its referenced collection when indexing. So for example when I search with the term "Concrete mixed with dried bamboo", it also returns the respective product as a result (the one with uid=LPK52). Please give me the solution?
You can first search for product types by name ("Concrete mixed with dried bamboo") and that will give you type IDs, then search for products by type ("LPK52"). I am not aware of any shortcuts.
Or, if you're willing to maintain it when you edit a type, you can add a typeName field in your products and search directly in them by typeName.
There is no way to search in one collection and get results from another collection.
One option is to do what Stratubas describes: first find the product types matching your condition, then find the products for that type.
If you don't want to do that, you can consider duplicating the product type name into each product document as a field. That would allow you to immediately query on products based on their product type's name.
How would I go about filtering a set of records based on their child records.
Let's say I have a collection Item that has a field to another collection Bag called bagId. I'd like to find all Items where a field on Bags matches some clause.
I.e. db.Items.find( { "where bag.type:'Paper' " }) . How would I go about doing this in MongoDB. I understand I'd have to join on Bags and then link where Item.bagId == Bag._id
I used Studio3T to convert a SQL GROUP BY to a Mongo aggregate. I'm just wondering if there's any defacto way to do this.
Should I perform a data migration to simply include Bag.type on every Item document (don't want to get into the habit of continuously making schema changes everytime I want to sort/filter Items by Bag fields).
Use something like https://github.com/meteorhacks/meteor-aggregate (No luck with that syntax yet)
Grapher https://github.com/cult-of-coders/grapher I played around with this briefly and while it's cool I'm not sure if it'll actually solve my problem. I can use it to add Bag.type to every Item returned, but I don't see how that could help me filter every item by Bag.type.
Is this just one of the tradeoffs of using a NoSQL dbms? What option above is recommended or are there any other ideas?
Thanks
You could use the $in functionality of MongoDB. It would look something like this:
const bagsIds = Bags.find({type: 'paper'}, {fields: {"_id": 1}}).map(function(bag) { return bag._id; });
const items = Items.find( { bagId: { $in: bagsIds } } ).fetch();
It would take some testing if the reactivity of this solution is still how you expect it to work and if this would still be suitable for larger collections instead of going for your first solution and performing the migration.
Is there a possibility in Mongodb to update a document, based on a query for 'count' of docs in other collection?
I'm looking for options to make it an atomic operation.
To be more specific, the following is how the two collections are designed:
Books(Collection)
_id
Name
isReviewed [Boolean]
Reviews (Collection)
_id
bookID [_id in Books Collection]
Comments
I came up with this design as "Reviews" array for a book will keep growing and are mutable.
Now, there's a requirement to set isReviewed flag in "Book" document to "true" when there's a "Review" doc. created for respective Book. The flag will stay "true" as long as there's at least one associated review existing for a book.
The same flag will be set to "false", when there're no reviews existing for a book. (The default value for the flag is "false" when the Book doc. is created)
When a Review is deleted, count of reviews for a book is calculated to see if it can be set to "false". (if count is 0, then set to false).
This system is designed for a multi-user environment sharing all resources such as books and reviews. All Users are given read/write permissions on all resources.(I know it sounds weird to allow all users to be able to create/ read/ edit/ delete all reviews, But, Let's say that's the case functionally).
Now, considering the above case, how can I ensure I perform an update related to setting the "isReviewed" flag based on the 'count' in the 'Reviews' collection?
Is this a case which can't be solved without transactions (I mean, do I need RDBMS)? I'm open to redesign my collections as well.
Any help is appreciated, Thank you
since mongodb provides AUTOMICITY at document level, You can pre-Join your books and reviews collection as below :
db.Books.insert(
{
"_id" : 1,
"Name": "ABC",
"reviews": [ ],
"count" : 0
}
P.S : your document size should be within 16MB size at MAX.
if you are expecting your "reviews" will be more than that, you will have to split into
two collection and handle the case in your code.
According to the view collation documentation for CouchDB(
http://wiki.apache.org/couchdb/View_collation), member order does matter for collation. I was wondering if there is a way to disable this attribute such that collation order does not matter? I want to be able to "search" my views such that the documents that are emitted satisfy all the key ranges for the field.
here is some more on view collation for your reference: CouchDB sorting and filtering in the same view
Likewise, if it is possible to set CouchDB such that order does not matter for view collation, the following parameters used for the GET request should only emit docs where doc.phone_number == "ZZZZZZZ" , whereas right now it emits the documents that fall within the range of the first 3 keys and completely ignores the last key. This occurs because the last key has the least precedence in the current collation scheme.
startkey: [null,null,null,"ZZZZZZZ"],
endkey: ["\ufff0","\ufff0","\ufff0","ZZZZZZZZ"],
Sample Mapping Function
var map = function(doc) {
/*
//Keys emitted
1. name
2. address
3. age
3. phone_number
*/
emit([doc.name,doc.address,doc.num_age,doc.phone_number],doc._id)
}
Is this possible, or do I have to create multiple views to perform this? The use of multiple views seems very inefficent.
I've read that CouchDB-Lucene:( How to realize complex search filters in couchdb? Should I avoid temporary views? )would be helpful for complex searching, but that doesn't seem applicable in this case.
Use of multiple views is not inefficient, quite to the contrary : having four views (name, address, age and phone number) will not use significantly more time or memory than having a single view emit everything. It is the simple, straightforward, efficient way of performing "WHERE field = value" queries in CouchDB.
If you are in fact looking for "WHERE field = value AND field2 = value2" queries, then CouchDB will not help you, and you will need to use Lucene.
You need to understand that the collation merely describes how keys are ordered. Even if you could specify any arbitrary collation, you will still have to deal with the fact that CouchDB need you to define an order for the keys, and only lets you query contiguous ranges of keys. This is not compatible with multi-dimensional range queries.
Here's an example of what I need in sql:
SELECT name FROM employ WHERE name LIKE %bro%
How do I create view like that in CouchDB?
The simple answer is that CouchDB views aren't ideal for this.
The more complicated answer is that this type of query tends to be very inefficient in typical SQL engines too, and so if you grant that there will be tradeoffs with any solution then CouchDB actually has the benefit of letting you choose your tradeoff.
1. The SQL Ways
When you do SELECT ... WHERE name LIKE %bro%, all the SQL engines I'm familiar with must do what's called a "full table scan". This means the server reads every row in the relevant table, and brute force scans the field to see if it matches.
You can do this in CouchDB 2.x with a Mango query using the $regex operator. The query would look something like this for the basic case:
{"selector":{
"name": {
"$regex": "bro"
}
}}
There do not appear to be any options exposed for case-sensitivity, etc. but you could extend it to match only at the beginning/end or more complicated patterns. If you can also restrict your query via some other (indexable) field operator, that would likely help performance. As the documentation warns:
Regular expressions do not work with indexes, so they should not be used to filter large data sets. […]
You can do a full scan in CouchDB 1.x too, using a temporary view:
POST /some_database/_temp_view
{"map": "function (doc) { if (doc.name && doc.name.indexOf('bro') !== -1) emit(null); }"}
This will look through every single document in the database and give you a list of matching documents. You can tweak the map function to also match on a document type, or to emit with a certain key for ordering — emit(doc.timestamp) — or some data value useful to your purpose — emit(null, doc.name).
2. The "tons of disk space available" way
Depending on your source data size you could create an index that emits every possible "interior string" as its permanent (on-disk) view key. That is to say for a name like "Dobros" you would emit("dobros"); emit("obros"); emit("bros"); emit("ros"); emit("os"); emit("s");. Then for a term like '%bro%' you could query your view with startkey="bro"&endkey="bro\uFFFF" to get all occurrences of the lookup term. Your index will be approximately the size of your text content squared, but if you need to do an arbitrary "find in string" faster than the full DB scan above and have the space this might work. You'd be better served by a data structure designed for substring searching though.
Which brings us too...
3. The Full Text Search way
You could use a CouchDB plugin (couchdb-lucene now via Dreyfus/Clouseau for 2.x, ElasticSearch, SQLite's FTS) to generate an auxiliary text-oriented index into your documents.
Note that most full text search indexes don't naturally support arbitrary wildcard prefixes either, likely for similar reasons of space efficiency as we saw above. Usually full text search doesn't imply "brute force binary search", but "word search". YMMV though, take a look around at the options available in your full text engine.
If you don't really need to find "bro" anywhere in a field, you can implement basic "find a word starting with X" search with regular CouchDB views by just splitting on various locale-specific word separators and omitting these "words" as your view keys. This will be more efficient than above, scaling proportionally to the amount of data indexed.
Unfortunately, doing searches using LIKE %...% aren't really how CouchDB Views work, but you can accomplish a great deal of search capability by installing couchdb-lucene, it's a fulltext search engine that creates indexes on your database that you can do more sophisticated searches with.
The typical way to "search" a database for a given key, without any 3rd party tools, is to create a view that emits the value you are looking for as the key. In your example:
function (doc) {
emit(doc.name, doc);
}
This outputs a list of all the names in your database.
Now, you would "search" based on the first letters of your key. For example, if you are searching for names that start with "bro".
/db/_design/test/_view/names?startkey="bro"&endkey="brp"
Notice I took the last letter of the search parameter, and "incremented" the last letter in it. Again, if you want to perform searches, rather than aggregating statistics, you should use a fulltext search engine like lucene. (see above)
You can use regular expressions. As per this table you can write something like this to return any id that contains "SMS".
{
"selector": {
"_id": {
"$regex": "sms"
}
}
}
Basic regex you can use on that includes
"sms$" roughly to LIKE "%sms"
"^sms" roughly to LIKE "sms%"
You can read more on regular expressions here
i found a simple view code for my problem...
{"getavailableproduct": {
"map": "function(doc) { var prefix = doc['productid'].match(/[A-Za-z0-9]+/g); if(prefix) for(var pre in prefix) { emit(prefix[pre],null); } }"
}
}
from this view code if i split a key sentence into a key word...
and i can call
?key="[search_keyword]"
but i need more complex code because if i run this code i can only find word wich i type (ex: eat, food, etc)...
but if i want to type not a complete word (ex: ea from eat, or foo from food) that code does not work..
I know it is an old question, but: What about using a "list" function? You can have all your normal views, andthen add a "list" function to the design document to process the view's results:
{
"_id": "_design/...",
"views": {
"employees": "..."
},
"lists": {
"by_name": "..."
}
}
And the function attached to "by_name" function, should be something like:
function (head, req) {
provides('json', function() {
var filtered = [];
while (row = getRow()) {
// We can retrive all row information from the view
var key = row.key;
var value = row.value;
var doc = req.query.include_docs ? row.doc : {};
if (value.name.indexOf(req.query.name) == 0) {
if (req.query.include_docs) {
filtered.push({ key: key, value: value, doc: doc});
} else {
filtered.push({ key: key, value: value});
}
}
}
return toJSON({ total_rows: filtered.length, rows: filtered });
});
}
You can, of course, use regular expressions too. It's not a perfect solution, but it works to me.
You could emit your documents like normal. emit(doc.name, null); I would throw a toLowerCase() on that name to remove case sensitivity.
and then query the view with a slew of keys to see if something "like" the query shows up.
keys = differentVersions("bro"); // returns ["bro", "br", "bo", "ro", "cro", "dro", ..., "zro"]
$.couch("db").view("employeesByName", { keys: keys, success: dealWithIt } )
Some considerations
Obviously that array can get really big really fast depending on what differentVersions returns. You might hit a post data limit at some point or conceivably get slow lookups.
The results are only as good as differentVersions is at giving you guesses for what the person meant to spell. Obviously this function can be as simple or complex as you like. In this example I tried two strategies, a) removed a letter and pushed that, and b) replaced the letter at position n with all other letters. So if someone had been looking for "bro" but typed in "gro" or "bri" or even "bgro", differentVersions would have permuted that to "bro" at some point.
While not ideal, it's still pretty fast since a look up in Couch's b-trees is fast.
why cann't we just use indexOf() in view?