CouchDB's Linked Documents in a View - couchdb

I'm having a hard time getting my head around CouchDB's linked documents feature.
I have two types of data being stored in a single CouchDB database:
{
"id":"1",
"type": "track",
"title": "Bohemian Rhapsody"
}
{
"id":"2",
"type": "artist",
"name": "Queen",
"tracks": ["1"]
}
I'm under the impression that I can write a view like the one below and get the following documents emited:
{
"id":"2",
"type": "artist",
"name": "Queen",
"tracks": [
{
"id":"1",
"type": "track",
"title": "Bohemian Rhapsody"
}
]
}
I've been trying this view, but it's not working the way I'm expecting:
function(doc) {
if(doc.type == 'artist') {
var tracks = [];
for(var i = 0; i < doc.tracks.length; i++) {
tracks.push({_id:doc.tracks[i]});
}
newdoc = eval(uneval(doc));
newdoc.tracks = tracks;
emit(doc._id,newdoc);
}
}
example here: http://jphastings.iriscouch.com/_utils/database.html?music/_design/test/_view/linked
This isn't returning what I'd hope - do you have any suggestions? Thanks

Okay I finally understand what you are trying to do.Yes this is possible.Here is how.
You have 2 documents
{
"_id":"anyvalue",
"type": "track",
"title": "Bohemian Rhapsody"
}
{
"_id":"2",
"type": "artist",
"name": "Queen",
"tracks": ["anyvalue"]
}
What you were doing wrong was not having quotes around the value of tracks(the item in the array).
2)The reference id must be _id for this to work.The difference is worth noting since you can have id field but only _id are used to identify documents.
For the result you want this view would suffice
function(doc) {
if (doc.type === 'artist') {
for (var i in doc.tracks) {
var id = doc.tracks[i];
emit(id, { _id: id });
}
}
}
What you want to be doing is use an emit function inside the for loop to emit the id field of the 'track' of every artist.
Then you want to query couch db view with the include_docs=true parameter.Here is the final result for the database that you created on iris couch.
http://jphastings.iriscouch.com/music/_design/test/_view/nested?reduce=false&include_docs=true
{
"total_rows": 3,
"offset": 0,
"rows": [
{
"id": "0b86008d8490abf0b7e4f15f0c6a50a7",
"key": "0b86008d8490abf0b7e4f15f0c6a463b",
"value": {
"_id": "0b86008d8490abf0b7e4f15f0c6a463b"
},
"doc": {
"_id": "0b86008d8490abf0b7e4f15f0c6a463b",
"_rev": "3-7e4ba3bfedd29a07898125c09dd7262e",
"type": "track",
"title": "Boheniam Rhapsody"
}
},
{
"id": "0b86008d8490abf0b7e4f15f0c6a50a7",
"key": "0b86008d8490abf0b7e4f15f0c6a5ae2",
"value": {
"_id": "0b86008d8490abf0b7e4f15f0c6a5ae2"
},
"doc": {
"_id": "0b86008d8490abf0b7e4f15f0c6a5ae2",
"_rev": "2-b3989dd37ef4d8ed58516835900b549e",
"type": "track",
"title": "Another one bites the dust"
}
},
{
"id": "0b86008d8490abf0b7e4f15f0c6a695e",
"key": "0b86008d8490abf0b7e4f15f0c6a6353",
"value": {
"_id": "0b86008d8490abf0b7e4f15f0c6a6353"
},
"doc": {
"_id": "0b86008d8490abf0b7e4f15f0c6a6353",
"_rev": "2-0383f18c198b813943615d2bf59c212a",
"type": "track",
"title": "Stripper Vicar"
}
}
]
}
Jason explains it wonderfully in this post
Best way to do one-to-many "JOIN" in CouchDB
this link is also helpful for entity relationships in couch db
http://wiki.apache.org/couchdb/EntityRelationship

Related

Duplicate a document DB using MongoDB Model

I want to duplicate a document from MongoDB Model in NodeJS, below is the structure
{
"_id": "62fe22f4b3c0fabfd1222d40", // this needs to be replaced in duplicated document
"id": 1, // this is auto increment field, needs to be generated new auto increment field
"type": "donut",
"name": "Cake",
"ppu": 0.55,
"batters":
{
"batter":
[
{ "id": "1001", "type": "Regular" },
{ "id": "1002", "type": "Chocolate" },
{ "id": "1003", "type": "Blueberry" },
{ "id": "1004", "type": "Devil's Food" }
]
},
"topping":
[
{ "id": "5001", "type": "None" },
{ "id": "5002", "type": "Glazed" },
{ "id": "5005", "type": "Sugar" },
{ "id": "5007", "type": "Powdered Sugar" },
{ "id": "5006", "type": "Chocolate with Sprinkles" },
{ "id": "5003", "type": "Chocolate" },
{ "id": "5004", "type": "Maple" }
]
}
I did refer this Duplicate a document in MongoDB using a new _id post, however I am not sure of resetting auto increment id
Assuming you use a NodeJS application you could do the following. Of course, if your id auto-increment is specified.:
const { _id, id, __v, ...newMyObj } = await MyModel.findOne({ id }).lean();
await MyModel.create(newMyObj);
I was able to fix the solution with below
const myDoc = await myModel.findOne({ id }).exec();
if (myDoc) {
const myDocObj = myDoc.toObject();
delete myDocObj._id;
delete myDocObj.id;
... continue deleting more unwanted props
return new myModel(myDocObj);
}
return null;

Get data from nested arrays two layers deep based on sub-index

I have this json schema
"header": {
"self": {},
"items": [
{
"_id": "5ec7e61979ec9914ecefc539",
"title": "Test",
"root": "true",
"alignment": "left",
"page": "test",
"translate": "",
"toggle": "",
"icon": "",
"IsActive": 1,
"submenu": [
{
"_id": "5ece913a353a71309084768d",
"title": "Sub Test",
"bullet": "dot",
"page": "test",
"translate": "MENU.TEST1",
"icon": "flaticon-stes-3",
"IsActive": 1
},
{
"_id": "5ece935d79972f0390997179",
"title": "Sub Test",
"bullet": "dot",
"page": "test",
"translate": "MENU.TEST2",
"icon": "flaticon-stes-3",
"IsActive": 1
}
]
}
]
}
// Index based on a previous query
this.db.collection('AssetData').find({"header.items.$.submenu.[0]._id":ObjectID("5ece913a353a71309084768d"));
//Tried with elemMatch
this.db.collection('AssetData').find(
{
"header.items": {
$elemMatch:{
"submenu": {
$elemMatch:{
"_id":ObjectID("5ece913a353a71309084768d")
}
}
}
}
});
And I am wanting to retrieve one of the sub-menu object data based on the _id from the sub-menu, but I'm having trouble retrieving it.
I'm not sure If I could use an index of the second array from another query to obtain the data, or if there's another way that I'm missing like elem match.
I am using MongoDB 3.5.6.
What would be the best way to retrieve this?
// Index based on a previous query
this.db.collection('AssetData').find({"header.items.$.submenu.[0]._id":ObjectID("5ece913a353a71309084768d")});
//Tried with elemMatch
this.db.collection('AssetData').find(
{
"header.items": {
$elemMatch:{
"submenu": {
$elemMatch:{
"_id":ObjectID("5ece913a353a71309084768d")
}
}
}
}
}).exec(function(err, item) {
console.log(item);
// here you can retrieve the stock from item, as you wish
});
hopefully it will help you

Map/reduce in CouchDB take a field if id contains a subString

I'll explain: I have this function
function (doc) {
if(doc.MovieId == "1721")
emit(doc.Rating, 1);
}
but it return me some document that are not relevant (for example they haven't the Rating field). My document _id is composed of partitionName:id, so I thought to do if(doc.MovieId == "1721" && doc._id.contains("ratings"){...} but it doesn't work.
Is there a way to do this?
-----EDIT 1-----
The docs in the circle are not relevant.
Do you need the schema of the JSON document?
-----EDIT 2-----
the following documents are NOT RELEVANT
1.
{
"_id": "movies : 1721",
"_rev": "1-d7e0e3c8152d6978073d280e0aef7457",
"MovieId": "1721",
"Title": "Titanic (1997)",
"Genres": [
"Drama",
"Romance"
]
}
2.
{
"_id": "tags : 1490",
"_rev": "1-14c20c9cfb3ee1964a298777f80333d5",
"MovieId": "1721",
"UserId": "474",
"Tag": "shipwreck",
"Timestamp": "1138031879"
}
3.
{
"_id": "tags : 2791",
"_rev": "1-e4d6c9573fcdae726a69d5fc6255de27",
"MovieId": "1721",
"UserId": "537",
"Tag": "romance",
"Timestamp": "1424141922"
}
documets like this are RELEVANT:
{
"_id": "ratings : 31662",
"_rev": "1-446665286337faaf51e23e40b527ec2d",
"MovieId": "1721",
"UserId": "219",
"Rating": "0.5",
"Timestamp": "1214043346"
}
Following view should just emit documents whose _id starts with "ratings :":
function (doc) {
var id_prefix = "ratings :";
if(doc._id.substr(0, id_prefix.length) === id_prefix && doc.MovieId == "1721")
emit(doc.Rating, 1);
}

ElasticSearch More_Like_This API and Nested Object Properties

I'm fairly new to ES so forgive me if this is a common scenario that I haven't discovered yet.
I've got an index which contains documents and each document can be related to a specific event, so I have a nested event object in each document. Event -> Document is a one-to-many relationship.
When I am displaying a single document, I want to show "More like this" where more-like-this actually represents more documents from the same meeting, by the same author, or on the same topic.
Author and Topic work fine, but although mlt_fields accepts "event.title" as a fieldname, it doesn't ever find any documents from the same event.
My mapping:
{
"myindex": {
"mappings": {
"myitem": {
"properties": {
"authors": {
"type": "string",
"analyzer": "keyword"
},
"id": {
"type": "integer"
},
"title": {
"type": "string"
},
"topics": {
"type": "string",
"analyzer": "keyword"
},
"event": {
"type": "nested",
"properties": {
"title": {
"type": "string",
"analyzer": "keyword"
},
...
}
}
}
}
}
}
}
My Query:
GET /myindex/mydoc/7/_mlt?mlt_fields=event.title&min_doc_freq=1&min_term_freq=1&percent_terms_to_match=0
{
"from": 0,
"size": 5
}
My Results:
{
...
"hits": {
"total": 0,
"max_score": null,
"hits": []
}
}
I suspect this is a nesting thing, so do I have to create a nested filtered query with "event = my_event" OR more-like-this = "author or topic" ? Or am I just missing something really stupid ?
Yes, you have to do a nested query to query nested fields:
Because nested docs are always masked to the parent doc, the nested docs can never be accessed outside the scope of the nested query.
http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/mapping-nested-type.html
http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-nested-query.html

How to return a summary of distinct values held within CouchDB

Given a collection of objects held within CouchDB that look like this:
{
"_id": "-5739365454",
"_rev": "1-d55b2651b214113bab0b4c18a401eaef",
"userId": "mike",
"remoteAddr": "77.102.112.205"
}
{
"_id": "-573936626",
"_rev": "1-778bbf1c0e5cfe8bbd83dbe618e8b549",
"userId": "mike",
"remoteAddr": "77.102.112.205"
}
...
{
"_id": "-573954545",
"_rev": "1-4350856f40c091156f8bb984a5559ebd",
"userId": "jeff",
"remoteAddr": "77.102.112.205"
}
I wish to create an output view that give me a set of results that looks something like the following (pseudo output - not syntactically valid):
userId count
mike 2
jeff 1
...
So, one 'row'/element would exist in the output for each distinct userId in the DB, along with the number of times that userId exists in the DB.
So far I have the following:
{
"_id": "_design/myObj",
"_rev": "4-cd468634f4fb6bef500753fedcb35e27",
"views": {
"by_userId": {
"map": "function(doc) { if (doc.userId) { emit (doc.userId, doc._id)} }",
"reduce": "_stats"
}
},
"language": "javascript"
}
... but this is not working as required. Any assistance gratefully received!
You could try use _count built-in function. For instance:
`
{
"_id": "_design/myObj",
"_rev": "4-cd468634f4fb6bef500753fedcb35e27",
"views": {
"by_userId": {
"map": "function(doc) { if (doc.userId) { emit (doc.userId, null)} }",
"reduce": "_count"
}
},
"language": "javascript"
}`

Resources