Skip to content

Async iteration #459

Description

@ziflex

Changes

This proposal introduces:

  • New keyword
  • New data structure
  • Another returned value from a program execution

Background

Every time you scrape a website, Ferret does not return result until query execution completes. It keeps all results in memory and if an error occurs - all data is lost.
I.e. you either have all data or nothing.

Proposal

This proposal offers an optional alternative approach - asynchronous execution using streams.
Instead of keeping data in memory and waiting for the end of execution, we could return the results as soon as they arrive using stream based data structures.
On each iteration we would push results to a stream and a caller of a compiled query would receive it.
In situations when having all data is not critical, it will improve stability and efficiency.

Here is a possible syntax for an async execution:

import (
	"context"
	"encoding/json"
	"fmt"
	"os"

	"github.com/MontFerret/ferret/pkg/compiler"
	"github.com/MontFerret/ferret/pkg/drivers"
	"github.com/MontFerret/ferret/pkg/drivers/cdp"
	"github.com/MontFerret/ferret/pkg/drivers/http"
)

func main() {
    query := `
        LET doc = DOCUMENT('https://www.theverge.com/tech', { driver: "cdp" })
        WAIT_ELEMENT(doc, '.c-compact-river__entry', 5000)
        LET articles = ELEMENTS(doc, '.c-entry-box--compact__image-wrapper')
        LET links = (
            FOR article IN articles
                RETURN article.attributes.href
        )
        
        FOR link IN links
            // The Verge has pretty heavy pages, so let's increase the navigation wait time
            NAVIGATE(doc, link, 20000)
            WAIT_ELEMENT(doc, '.c-entry-content', 5000)
            LET texter = ELEMENT(doc, '.c-entry-content')
            YIELD texter.innerText
`

	comp := compiler.New()

	program, err := comp.Compile(query)

	if err != nil {
		panic(err)
	}

	ctx := drivers.WithContext(context.Background(), cdp.NewDriver())

	out, err := program.Run(ctx)

	if err != nil {
		panic(err)
	}

     // .Run now returns io.Reader interface
     // Even if a query does not use ASYNC iteration 
     data, err := ioutils.ReadAll(out)
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions